package org.jsola.hr.provider.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.jsola.admin.dao.IDefaultVariableDAO;
import org.jsola.admin.entity.DefaultFieldDO;
import org.jsola.admin.entity.DefaultVariableDO;
import org.jsola.admin.entity.HeaderFieldsDO;
import org.jsola.admin.service.IAsyncTaskDetailService;
import org.jsola.admin.service.IAsyncTaskService;
import org.jsola.admin.service.IDefaultVariableService;
import org.jsola.admin.vo.HeaderFieldsListVO;
import org.jsola.admin.vo.HeaderFieldsVO;
import org.jsola.cache.ICache;
import org.jsola.common.BeanKit;
import org.jsola.common.DateKit8;
import org.jsola.common.PageKit;
import org.jsola.common.StrKit;
import org.jsola.contract.constant.AccountType;
import org.jsola.contract.constant.ContractConstants;
import org.jsola.contract.constant.SignMethod;
import org.jsola.contract.core.MockMultipartFile;
import org.jsola.contract.dao.ICategoryDAO;
import org.jsola.contract.dao.ITemplateDAO;
import org.jsola.contract.dao.ITemplateVariableDAO;
import org.jsola.contract.dto.*;
import org.jsola.contract.entity.*;
import org.jsola.contract.query.*;
import org.jsola.contract.service.*;
import org.jsola.contract.vo.*;
import org.jsola.core.Page;
import org.jsola.core.Result;
import org.jsola.core.entity.BaseDO;
import org.jsola.exception.ParamException;
import org.jsola.file.core.FileProperties;
import org.jsola.file.entity.FileDO;
import org.jsola.file.entity.FileVersionDO;
import org.jsola.file.service.IFileService;
import org.jsola.file.service.IFileVersionService;
import org.jsola.file.service.ISiteFileService;
import org.jsola.hr.common.*;
import org.jsola.hr.constant.ContractDateEnum;
import org.jsola.hr.constant.FileType;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.constant.JunZiQianConstants;
import org.jsola.hr.core.JunZiQianProperties;
import org.jsola.hr.dto.TemplateVariableUpdateDTO;
import org.jsola.hr.dto.*;
import org.jsola.hr.dto.contract.AddSealDTO;
import org.jsola.hr.dto.contract.UpdateSealDTO;
import org.jsola.hr.dto.contract.VerifyEnterpriseDTO;
import org.jsola.hr.entity.*;
import org.jsola.hr.exception.HrException;
import org.jsola.hr.provider.*;
import org.jsola.hr.query.EmpInfoExportColumn;
import org.jsola.hr.query.HrContractExportQuery;
import org.jsola.hr.query.HrContractQuery;
import org.jsola.hr.service.*;
import org.jsola.hr.service.impl.AsyncCompanyFature;
import org.jsola.hr.vo.*;
import org.jsola.hr.vo.excel.ContractExportVO;
import org.jsola.hr.vo.junziqian.JunZiQianResult;
import org.jsola.hr.vo.junziqian.SealVariableVO;
import org.jsola.hr.vo.junziqian.UrlVO;
import org.jsola.log.constant.ActionType;
import org.jsola.orm.filter.Order;
import org.jsola.user.core.TokenUser;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.time.Period;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
 * @author JanusMix
 */
@Slf4j
@Service("hrContractProviderService")
public class ContractProviderServiceImpl implements IContractProviderService {

    @Autowired
    private ISealService sealService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IVariableService variableService;
    @Autowired
    private IPartySignService partySignService;
    @Autowired
    private IContractService contractService;
    @Autowired
    private ICategoryService categoryService;
    @Autowired
    private IThirdSignService thirdSignService;
    @Autowired
    private ITemplateVariableService templateVariableService;
    @Autowired
    private ITemplateService templateService;
    @Autowired
    private FileProperties fileProperties;
    @Autowired
    private ISiteFileService siteFileService;

    @Autowired
    private IAppendixService appendixService;

    @Autowired
    private IFileProviderService fileProviderService;

    @Autowired
    private ICompanyService companyService;

    @Autowired
    private IContractCredentialSiteService contractCredentialSiteService;

    @Autowired
    private IEmpPaperContractService empPaperContractService;

    @Autowired
    private IEmpContractService empContractService;

    @Autowired
    private IContractOverviewProviderService contractOverviewProviderService;

    @Autowired
    private IContractConfigSiteService contractConfigSiteService;

    @Autowired
    private ISignService signService;

    @Autowired
    private IEmpInfoService empInfoService;

    @Autowired
    private ICompanyAdditionalFieldsService companyAdditionalFieldsService;

    @Autowired
    private ICompanyNecessaryFieldService companyNecessaryFieldService;

    @Autowired
    private IAdditionalVariableService additionalVariableService;

    @Autowired
    private INecessaryVariableService necessaryVariableService;

    @Autowired
    private IContractGroupService contractGroupService;

    @Autowired
    private ISealPositionService sealPositionService;

    @Autowired
    private IAdminProviderService adminProviderService;

    @Autowired
    private IAsyncTaskDetailService asyncTaskDetailService;

    @Autowired
    private IFileVersionService fileVersionService;

    @Autowired
    private IFileService fileService;

    private static final String DATE_STR = "class java.util.Date";

    @Autowired
    private ICategoryDAO categoryDAO;
    @Autowired
    private ITemplateVariableDAO templateVariableDAO;
    private final String ADMIN = "admin";
    @Autowired
    private ITemplateDAO templateDAO;
    @Autowired
    private IDefaultVariableService defaultVariableService;
    @Autowired
    private IDefaultVariableDAO defaultVariableDAO;

    @Autowired
    private AsyncCompanyFature asyncCompanyFature;

    @Autowired
    private AsyncContractProvider asyncContractProvider;

    @Autowired
    private IAsyncTaskService asyncTaskService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private JunZiQianProperties junZiQianProperties;

    @Autowired
    private ICompanyContractQuotaService companyContractQuotaService;

    @Autowired
    private ICache cache;
    private static String KEY_PERSON = "contract:url:person:";
    private static String KEY_COMPANY = "contract:url:company:";

    @Override
    public SealVO saveSeal(AddSealDTO addSealDTO, TokenUser tokenUser) {
        String companyId = addSealDTO.getCompanyId();
        SealAddDTO sealAddDTO = addSealDTO.to(SealAddDTO.class);
        //查询公司账户
        AccountDO accountDO = accountService.findAccount(AccountType.ENTERPRISE, companyId);
        if (accountDO != null) {
            sealAddDTO.setAccountId(accountDO.getId());
            sealAddDTO.setStatus(accountDO.getReal() ? ContractConstants.SEAL_STATUS_VERIFIED : ContractConstants.SEAL_STATUS_UNVERIFIED);
        } else {
            sealAddDTO.setStatus(ContractConstants.SEAL_STATUS_UNVERIFIED);
        }
        sealAddDTO.setSubjectId(companyId);
        //保存印章
        return sealService.save(sealAddDTO, tokenUser);
    }

    @Override
    public int updateSeal(UpdateSealDTO updateSealDTO, TokenUser tokenUser) {
        SealUpdateDTO sealUpdateDTO = updateSealDTO.to(SealUpdateDTO.class);
        return sealService.update(sealUpdateDTO, tokenUser);
    }

    @Override
    public SealDO selectSealByCompanyId(String companyId, String siteId) {
        SealQuery sealQuery = new SealQuery();
        sealQuery.setSubjectId(companyId);
        List<SealDO> sealDOS = sealService.selectDO(sealQuery, siteId);
        if (CollectionUtils.isEmpty(sealDOS)) {
            return null;
        }
        return sealDOS.get(0);
    }

    @Override
    public SealVO selectSealById(Long sealId, String siteId) {
        return sealService.selectById(sealId, siteId);
    }

    @Override
    public List<SealDO> selectSealByIds(List<Long> sealIds, String siteId) {
        return sealService.selectByIds(sealIds, siteId);
    }

    @Override
    public List<SealListVO> sealSealList(SealQuery sealQuery, String siteId) {
        return sealService.select(sealQuery, siteId);
    }

    @Override
    public void verifyEnterprise(VerifyEnterpriseDTO verifyEnterpriseDTO, TokenUser tokenUser) {
        CompanyContractQuotaVO quotaVO = companyContractQuotaService.selectById(verifyEnterpriseDTO.getOrganizationId(), tokenUser.getSiteId());
        HrException.isTrue(quotaVO != null && Objects.equals(quotaVO.getStatus(), 1), "企业未认证，请联系管理员处理");
        Long sealId = verifyEnterpriseDTO.getSealId();
        SealVO sealVO = sealService.selectById(sealId, tokenUser.getSiteId());
        Long organizationId = quotaVO.getId();
        //添加企业账户
        AccountDO accountDO = thirdSignService.addEnterpriseAccount(String.valueOf(organizationId), quotaVO.getCompanyName(),
                String.valueOf(quotaVO.getOrganizationType()), quotaVO.getOrganizationRegNo(), quotaVO.getLegalName(),
                quotaVO.getOrganizationRegNo(), quotaVO.getEmail(), tokenUser);
        // 请求君子签认证印章
        String url = junZiQianProperties.getServicesUrl() + JunZiQianConstants.URL_COMPANY_UPLOAD_ENT_SIGN;
        SealVariableVO vo = new SealVariableVO();
        vo.setSignName(sealVO.getName());
        vo.setEmail(quotaVO.getEmail());
        vo.setSignImgFile(Base64Kit.base64ToFile(sealVO.getSealString()));
        JunZiQianResult result = JunZiQianUtil.send(vo, junZiQianProperties, url);

        //更新印章状态
        SealDO sealDO = new SealDO();
        sealDO.setId(sealId);
        sealDO.setAccountId(accountDO.getId());
        sealDO.setOrganizationId(organizationId);
        sealDO.setThirdSealId(String.valueOf(result.getData()));
        sealDO.setStatus(ContractConstants.SEAL_STATUS_VERIFIED);
        sealService.updateByIdSelective(sealDO, tokenUser.getSiteId(), tokenUser.getUserId());
        SealDO newSeal = sealService.selectDOById(sealDO.getId(), tokenUser.getSiteId());
        try {
            // 保存log日志
            ILogProviderService logProviderService = BeanKit.getBean(ILogProviderService.class);
            if (logProviderService == null) {
                log.error("logProviderService is null");
                return;
            }
            logProviderService.log(newSeal.getName() + "由管理员" + tokenUser.getUserName() +
                            " 认证合同印章", ActionType.UPDATE.getValue(), "Seal"
                    , newSeal.getId() + "", newSeal.getSubjectId(), tokenUser);
        } catch (Exception e) {
            log.error("hr保存日志出错了", e);
        }
    }

    @Override
    public void verifyEmployee(String userId, String name, String phone, String idNo, String personArea, TokenUser tokenUser) {
        //添加个人账户
        thirdSignService.addPersonAccount(userId, name, phone, idNo, personArea, tokenUser);
    }

    @Override
    public List<VariableListVO> selectAllVariable() {
        return variableService.select(new VariableQuery());
    }

    @Override
    public List<TemplateVariableListVO> selectVariableByTemplateId(Long templateId, String siteId) {
        TemplateVariableQuery templateVariableQuery = new TemplateVariableQuery();
        templateVariableQuery.setTemplateId(templateId);
        List<Order> orders = new ArrayList<>();
        Order order = new Order();
        order.setProperty("id");
        order.setDesc(Boolean.FALSE);
        orders.add(order);
        templateVariableQuery.setOrders(orders);
        return templateVariableService.select(templateVariableQuery, siteId);
    }

    @Override
    public List<PartySignListVO> selectUserContractList(TokenUser tokenUser) {
        //查询用户账户
        AccountDO accountDO = accountService.findAccount(AccountType.PERSON, tokenUser.getUserId());
        if (accountDO == null || !accountDO.getReal()) {
            throw new ParamException("用户未认证");
        }
        PartySignQuery partySignQuery = new PartySignQuery();
        partySignQuery.setAccountId(accountDO.getId());
//        partySignQuery.setAppAllShow(false);
        List<PartySignListVO> partySignList = partySignService.select(partySignQuery, tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(partySignList)) {
            // 小程序端员工签署完成,显示状态就是签署完成
            partySignList.parallelStream().forEach(
                    partySign -> {
                        //  1待员工签署，2待公司签署，3签署完成
                        if (partySign.getContractStatus() != null && partySign.getContractStatus() == 2) {
                            partySign.setContractStatus(3);
                        }
                    }
            );
        }
        return partySignList;
    }

    @Override
    public CategoryVO selectCategoryById(Long categoryId, String siteId) {
        CategoryVO categoryVO = categoryService.selectById(categoryId, siteId);
        //如果有企业签署
        if (!SignMethod.PERSON_ONLY.getValue().equals(categoryVO.getSignType())) {
            ParamException.notNull(categoryVO.getEnterpriseSealId(), "企业印章未开通");
        }
        return categoryVO;
    }

    @Override
    public List<CategoryDO> selectCategoryByIds(List<Long> categoryIds) {
        return categoryService.selectByIds(categoryIds);
    }

    @Override
    public CategoryVO saveCategory(CategoryAddDTO categoryAddDTO, TokenUser tokenUser) {
        return categoryService.save(categoryAddDTO, tokenUser);
    }

    @Override
    public List<CategoryDO> listBySubjectId(String subjectId, String siteId) {
        return categoryService.listBySubjectId(subjectId, siteId);
    }

    @Override
    public List<Map<String, Object>> getVariableMapList(Long templateId, String siteId) {
        Map<String, Object> tempMap;
        // 员工必输
        List<NecessaryVariableDO> necessaryVariableDOList =
                necessaryVariableService.selectByTemplateId(templateId, siteId);
        List<String> necessaryIds = necessaryVariableDOList.parallelStream().map(NecessaryVariableDO::getNecessaryId)
                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
        List<CompanyNecessaryFieldDO> companyNecessaryFieldDOList
                = companyNecessaryFieldService.selectDOByIds(necessaryIds, siteId);
        // 基本信息
        List<Map<String, Object>> basicList = new ArrayList<>();
        // 联系信息
        List<Map<String, Object>> contactList = new ArrayList<>();
        // 其他信息
        List<Map<String, Object>> otherList = new ArrayList<>();
        // 变量名
        List<String> fieldNameList = new ArrayList<>();
        for (CompanyNecessaryFieldDO companyNecessaryFieldDO : companyNecessaryFieldDOList) {
            tempMap = new HashMap<>(1);
            Map<String, Object> map = new HashMap<>(16);
            map.put("necessaryId", companyNecessaryFieldDO.getId());
            String fieldName = companyNecessaryFieldDO.getFieldName();
            if ("basic".equals(companyNecessaryFieldDO.getModuleCode()) && "姓名".equals(fieldName)) {
                fieldName = "员工姓名";
            } else if ("empFamily".equals(companyNecessaryFieldDO.getModuleCode())) {
                if (!fieldName.contains("(家庭成员)")) {
                    fieldName += "(家庭成员)";
                }
            } else if ("empContacts".equals(companyNecessaryFieldDO.getModuleCode())) {
                if (!fieldName.contains("(紧急联系人)")) {
                    fieldName += "(紧急联系人)";
                }
            }
            fieldNameList.add(fieldName);
            map.put("name", fieldName);
            map.put("content", null);
            map.put("moduleCode", companyNecessaryFieldDO.getModuleCode());
            tempMap.put(fieldName, map);
            if ("basic".equals(companyNecessaryFieldDO.getModuleCode())) {
                basicList.add(tempMap);
            } else if ("contact".equals(companyNecessaryFieldDO.getModuleCode())) {
                contactList.add(tempMap);
            } else {
                otherList.add(tempMap);
            }
        }
        // 员工自定义
        List<AdditionalVariableDO> additionalVariableDOList =
                additionalVariableService.selectByTemplateId(templateId, siteId);
        List<Long> additionalIds = additionalVariableDOList.parallelStream().map(AdditionalVariableDO::getAdditionalId)
                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
        List<CompanyAdditionalFieldsDO> companyAdditionalFieldsDOList
                = companyAdditionalFieldsService.selectDOByIds(additionalIds, siteId);
        for (CompanyAdditionalFieldsDO companyAdditionalFieldsDO : companyAdditionalFieldsDOList) {
            tempMap = new HashMap<>(1);
            Map<String, Object> map = new HashMap<>(16);
            map.put("additionalId", companyAdditionalFieldsDO.getId());
            map.put("content", null);
            String fieldName = companyAdditionalFieldsDO.getFieldName();
            if ("basic".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(基本信息)";
            } else if ("contact".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(联系信息)";
            } else if ("empState".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(员工状态)";
            } else if ("onJob".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(在职信息)";
            } else if ("file".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(档案信息)";
            } else if ("retire".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(退休信息)";
            } else if ("annex".equals(companyAdditionalFieldsDO.getModuleCode())) {
                fieldName += "(材料附件)";
            }
            fieldNameList.add(fieldName);
            map.put("name", fieldName);
            // 自定义字段自己成一组
            map.put("moduleCode", "additional");
            tempMap.put(fieldName, map);
            basicList.add(tempMap);
        }

        List<Map<String, Object>> variableMapList = new ArrayList<>();
        variableMapList.addAll(basicList);
        variableMapList.addAll(contactList);
        variableMapList.addAll(otherList);
        //合同属性集合
        List<TemplateVariableListVO> templateVariableListVOList = this.selectVariableByTemplateId(templateId, siteId);
        for (TemplateVariableListVO templateVariableListVO : templateVariableListVOList) {
            // 若员工信息中有同名字段 取员工信息 而非合同模板
            if (fieldNameList.contains(templateVariableListVO.getVariableName())) {
                continue;
            }
            tempMap = new HashMap<>(1);
            Map<String, Object> map = new HashMap<>(16);
            map.put("id", templateVariableListVO.getVariableId());
            map.put("name", templateVariableListVO.getVariableName());
            map.put("content", null);
            map.put("moduleCode", templateVariableListVO.getVariableType());
            tempMap.put(templateVariableListVO.getVariableName(), map);
            variableMapList.add(tempMap);
        }
        return variableMapList;
    }

    @Override
    public TemplateDO getTemplateById(Long templateId, String siteId) {
        return templateService.selectDOById(templateId, siteId);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void updateUserPhone(String oldPhone, String memberId, String newPhone, TokenUser tokenUser) {
        accountService.updateUserPhone(oldPhone, memberId, newPhone, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public ContractCountVO getContractCount(Integer contractType, String subjectId, TokenUser tokenUser) {
        return contractService.getContractCount(contractType, subjectId, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public ContractCountVO getContractCount(Integer contractType, List<String> subjectIds, TokenUser tokenUser) {
        return contractService.getContractCount(contractType, subjectIds, tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public FileVersionDO downloadContract(HrContractDownloadDTO hrContractDownloadDTO, TokenUser tokenUser) {
        // 用工单位
        CompanyDO companyDO = null;
        if (StringUtils.isNotBlank(hrContractDownloadDTO.getSubjectId())) {
            companyDO = companyService.selectDOById(hrContractDownloadDTO.getSubjectId(), tokenUser.getSiteId());
            ParamException.notNull(companyDO, "用工单位不存在或已删除");
        }

        // 查询要下载的合同
        List<ContractDO> contractList;
        if (CollectionUtils.isEmpty(hrContractDownloadDTO.getContractIdList())) {
            // 没有指定,根据条件查询下载
            ContractQuery contractQuery = hrContractDownloadDTO.to(ContractQuery.class);
            // 查询导出的数量  超过100 不让导出
            int count = contractService.selectCount(contractQuery, tokenUser.getSiteId());
            if (count > 500) {
                throw new ParamException("一次导出请勿超过500条!");
            }
            contractList = contractService.selectNoContentDO(contractQuery, tokenUser.getSiteId());
        } else {
            if (hrContractDownloadDTO.getContractIdList().size() > 500) {
                throw new ParamException("一次导出请勿超过500条!");
            }
            contractList = contractService.selectNoContentByIds(hrContractDownloadDTO.getContractIdList());
        }
        ParamException.notEmpty(contractList, "没有需要打包下载的合同,请检查");
        // 如果查询参数有 员工状态  查询账号乙方id的userId 查找该租户的empInfo 判断状态
        Integer empInfoStatus = hrContractDownloadDTO.getEmpStatus();
        String idCard = hrContractDownloadDTO.getIdCard();
        if (empInfoStatus != null || StrKit.isNotEmpty(idCard)) {
            contractList.removeIf(contractDO -> {
                // 根据账号id查询用户id
                String accountId = contractDO.getPartyBAccountId();
                String companyId = contractDO.getSubjectId();
                if (StrKit.isEmpty(accountId) || StrKit.isEmpty(companyId)) {
                    return true;
                }
                AccountDO accountDO = accountService.selectDOById(accountId);
                if (Objects.isNull(accountDO) || StrKit.isEmpty(accountDO.getMemberId())) {
                    return true;
                }
                String userId = accountDO.getMemberId();
                // 根据userId、companyId查询empInfo（多个取最新的一条）
                EmpInfoDO empInfoDO = empInfoService.selectByUserIdAndCompanyId(userId, companyId);
                if (empInfoDO == null) {
                    return true;
                }
                if (empInfoStatus != null) {
                    return !empInfoStatus.equals(empInfoDO.getStatus());
                } else {
                    return empInfoDO.getIdCard() == null || !empInfoDO.getIdCard().contains(idCard);
                }
            });
        }
        List<Long> contractIdList = contractList.parallelStream().map(BaseDO::getId).collect(Collectors.toList());
        Map<String, String> signNoIdCardMap = getSignNoIdCardMap(contractList, tokenUser.getSiteId());
        // 合同文件
        List<AppendixDO> appendixList = appendixService.listByContractIds(contractIdList, tokenUser.getSiteId());
        ParamException.notEmpty(appendixList, "没有需要打包下载的合同文件,请检查");
        // 要下载的合同文件路径
        List<String> fileUrlList = appendixList.parallelStream()
                .map(AppendixDO::getPath)
                .collect(Collectors.toList());
        String zipName;
        if (null != companyDO) {
            zipName = companyDO.getName() + "的合同";
        } else {
            zipName = "合同";
        }
        return fileProviderService.download(fileUrlList, zipName, signNoIdCardMap, tokenUser);
    }

    /**
     * 获取签署编号-身份证对应关系
     *
     * @param contractList 合同列表
     * @param siteId       站点Id
     * @return 签署编号-身份证Map
     */
    private Map<String, String> getSignNoIdCardMap(List<ContractDO> contractList, String siteId) {
        List<HrContractListVO> hrContractListVOList = listToHrContract(contractList.parallelStream()
                        .map(contractDO -> contractDO.to(ContractListVO.class)).collect(Collectors.toList())
                , new TokenUser("", siteId));
        return hrContractListVOList.parallelStream().collect(Collectors.toMap(ContractListVO::getSignNo
                , HrContractListVO::getIdCard));
    }


    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void deleteContractAndRelationById(Long contractId, TokenUser tokenUser) {
        contractService.deleteContractAndRelationById(contractId, tokenUser);
    }

    @Override
    public boolean isSiteContractEnable(TokenUser tokenUser) {
        return contractCredentialSiteService.selectSiteCredential(tokenUser.getSiteId()) != null;
    }

    @Override
    public ContractDO selectAllSignedRecording(String userId, String subjectId, String empId, TokenUser tokenUser) {
        //根据用户的userId去account表查到对应的account表的Id
        List<AccountDO> accountDOList = accountService.findAccounts(AccountType.PERSON, Collections.singletonList(userId));

        //如果account表Id没有代表这个用户没有合同签署记录
        if (CollectionUtils.isEmpty(accountDOList)) {
            return null;
        }
        List<ContractDO> contractDOList = contractService.listNoContent(subjectId, accountDOList.get(0).getId(),
                tokenUser.getSiteId());
        List<ContractDO> contractDOListSort = contractDOList.stream()
                .filter(contract -> contract.getStatus() != -1)
                // 创建时间倒序
                .sorted(Comparator.comparing(ContractDO::getGmtCreate).reversed())
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(contractDOListSort)) {
            return null;
        }
        return contractDOListSort.get(0);
    }

    @Override
    public ContractDO selectAllSignedRecordingByCompanyIdAndEmpId(String companyId, String empId, TokenUser tokenUser) {
        String siteId = tokenUser.getSiteId();
        List<EmpContractDO> empContractDOList = empContractService.listByEmpId(empId, siteId);
        ContractDO contractDO = new ContractDO();
        // 设置电子合同期限,单位:月
        int dateline = 0;
        if (!CollectionUtils.isEmpty(empContractDOList)) {
            EmpContractDO empContractDO = empContractDOList.get(0);
            contractDO.setFileType(empContractDO.getType());
            contractDO.setSignDate(empContractDO.getStartTime());
            contractDO.setEndDate(empContractDO.getEndTime());
            Integer termYear = empContractDO.getTermYear();
            Integer termMonth = empContractDO.getTermMonth();

            if (null != termYear) {
                dateline += termYear * 12;
            }
            if (null != termMonth) {
                dateline += termMonth;
            }
            contractDO.setDateline(dateline);
            return contractDO;
        }
        // 查询纸质合同
        List<EmpPaperContractDO> empPaperContractDOList = empPaperContractService.listContractByEmpId(empId,
                tokenUser.getSiteId());
        List<EmpPaperContractDO> empPaperContractDOListSort = empPaperContractDOList.stream()
                .filter(empContractDO -> null != empContractDO.getEndTime())
                .sorted(Comparator.comparing(EmpPaperContractDO::getEndTime).reversed())
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(empPaperContractDOListSort)) {
            EmpPaperContractDO empPaperContractDO = empPaperContractDOListSort.get(0);
            contractDO.setFileType(empPaperContractDO.getType());
            contractDO.setSignDate(empPaperContractDO.getStartTime());
            contractDO.setEndDate(empPaperContractDO.getEndTime());
            Integer timeLimitType = empPaperContractDO.getTimeLimitType();
            // 合同期限
            int timeLimit = empPaperContractDO.getTimeLimit() == null ? 0 : empPaperContractDO.getTimeLimit();

            if (ContractDateEnum.YEAR.getValue().equals(timeLimitType)) {
                dateline = timeLimit * 12;
            } else if (ContractDateEnum.MONTH.getValue().equals(timeLimitType)) {
                dateline = timeLimit;
            }
            contractDO.setDateline(dateline);
            return contractDO;
        }
        return null;
    }

    @Override
    public String selectExportContractList(HrContractExportQuery hrContractExportQuery
            , TokenUser tokenUser) {
        long l = System.currentTimeMillis();
        ContractQuery contractQuery = hrContractExportQuery.to(ContractQuery.class);
        List<ContractDO> contractList = contractService.selectNoContentDO(contractQuery, tokenUser.getSiteId());
        List<ContractListVO> contractListVOList = contractList.stream().map(contractDO ->
                contractDO.to(ContractListVO.class)).collect(Collectors.toList());
        long l1 = System.currentTimeMillis();
        log.info("管理后台导出合同查询耗时：{}ms", l1 - l);
        ParamException.notEmpty(contractListVOList, "没有需要导出的数据，请调整检索条件");
        // 如果查询参数有 员工状态  查询账号乙方id的userId 查找该租户的empInfo 判断状态
        Integer empInfoStatus = hrContractExportQuery.getEmpStatus();
        String idCard = hrContractExportQuery.getIdCard();
        if (empInfoStatus != null || StrKit.isNotEmpty(idCard)) {
            contractListVOList.removeIf(contractListVO -> {
                // 根据账号id查询用户id
                String accountId = contractListVO.getPartyBAccountId();
                String companyId = contractListVO.getSubjectId();
                if (StrKit.isEmpty(accountId) || StrKit.isEmpty(companyId)) {
                    return true;
                }
                AccountDO accountDO = accountService.selectDOById(accountId);
                if (Objects.isNull(accountDO) || StrKit.isEmpty(accountDO.getMemberId())) {
                    return true;
                }
                String userId = accountDO.getMemberId();
                // 根据userId、companyId查询empInfo（多个取最新的一条）
                EmpInfoDO empInfoDO = empInfoService.selectByUserIdAndCompanyId(userId, companyId);
                if (empInfoDO == null) {
                    return true;
                }
                if (empInfoStatus != null) {
                    return !empInfoStatus.equals(empInfoDO.getStatus());
                } else {
                    return empInfoDO.getIdCard() == null || !empInfoDO.getIdCard().contains(idCard);
                }
            });
        }
        ParamException.notEmpty(hrContractExportQuery.getExportColumns(), "没有需要导出的表头，请调整检索条件");
        List<HrContractListVO> hrContractListVOList = listToHrContract(contractListVOList, tokenUser);
        long l2 = System.currentTimeMillis();
        log.info("管理后台导出合同耗时：{}ms", l2 - l1);
        // 表头为中文名，所以需要再定义一个英文名的集合，准备反射获取属性
        List<List<String>> headList = new ArrayList<>();
        // 表头英文名集合
        List<String> headPropertyList = new ArrayList<>();
        // 定义所有的数据集合
        List<List<String>> dataList = new ArrayList<>();
        for (EmpInfoExportColumn empInfoExportColumn : hrContractExportQuery.getExportColumns()) {
            // 定义每一个表头
            List<String> oneHead = new ArrayList<>();
            oneHead.add(empInfoExportColumn.getTitle());
            headList.add(oneHead);
            headPropertyList.add(empInfoExportColumn.getDataIndex());
        }
        for (HrContractListVO hrContractListVO : hrContractListVOList) {
            // 定义每一条数据
            List<String> oneData = new ArrayList<>();
            //循环表头,往每一条数据中，拼装数据
            for (String head : headPropertyList) {
                Field field;
                try {
                    // 先取子类属性
                    field = hrContractListVO.getClass().getDeclaredField(head);
                } catch (NoSuchFieldException e) {
                    try {
                        // 子类没有从父类中取
                        field = hrContractListVO.getClass().getSuperclass().getDeclaredField(head);
                    } catch (NoSuchFieldException ex) {
                        log.error("表头传入错误：heard：{}", head);
                        throw new ParamException("表头传入错误");
                    }
                }
                field.setAccessible(true);
                try {
                    if (field.get(hrContractListVO) != null) {
                        if (field.getGenericType().toString().equals(DATE_STR)) {
                            // 日期类型
                            Date date = (Date) field.get(hrContractListVO);
                            oneData.add(DateKit8.format2Date(date));
                        } else {
                            String value = ContractFieldToString.fieldToString(field, hrContractListVO);
                            oneData.add(value);
                        }
                    } else {
                        oneData.add("");
                    }
                } catch (IllegalAccessException e) {
                    log.error("表头传入错误：heard：{}", head);
                    throw new ParamException("表头传入错误");
                }
            }
            dataList.add(oneData);
        }

        long l4 = System.currentTimeMillis();
        log.info("管理后台导出合同导出：整理表头,耗时：{}ms", (l4 - l2));
        String excelUrl = ExcelKit.exportExcelDynamic(hrContractExportQuery.getEnName(), "合同导出", dataList, headList);
        long l5 = System.currentTimeMillis();
        log.info("管理后台导出合同导出：写入{}条excel,耗时：{}ms", hrContractListVOList.size(), (l5 - l4));
        return excelUrl;
    }

    @Override
    public Page<ContractStatisticsEmpUnitsVO> selectComForRankCount(ContractQuery contractQuery, TokenUser tokenUser) {
        // 查询所有合同,过滤单位id为空的
        Page<ContractStatisticsEmpUnitsVO> empUnitsVOPage
                = contractOverviewProviderService.selectComForRankCount(contractQuery, tokenUser);
        List<ContractStatisticsEmpUnitsVO> entities = empUnitsVOPage.getEntities().stream()
                .filter(empUnitsVO -> StringUtils.isNotBlank(empUnitsVO.getSubjectId()))
                .collect(Collectors.toList());
        // 合同中的用工单位id
        List<String> subjectIdList = entities.parallelStream()
                .map(ContractStatisticsEmpUnitsVO::getSubjectId)
                .collect(Collectors.toList());
        // 查询所有公司
        List<CompanyDO> companyDOS = companyService.selectByIds(subjectIdList, tokenUser.getSiteId());
        Map<String, CompanyDO> idCompanyDOMap = companyDOS.parallelStream()
                .collect(Collectors.toMap(CompanyDO::getId, v -> v, (v1, v2) -> v1));
        // 过滤不存在的用工单位,并写入用工单位名称
        Iterator<ContractStatisticsEmpUnitsVO> iterator = entities.iterator();
        while (iterator.hasNext()) {
            ContractStatisticsEmpUnitsVO next = iterator.next();
            CompanyDO companyDO = idCompanyDOMap.get(next.getSubjectId());
            if (Objects.nonNull(companyDO)) {
                next.setSubjectName(companyDO.getName());
            } else {
                iterator.remove();
            }
        }
        return new Page<>(entities, contractQuery.getPageNo(), contractQuery.getPageSize(), entities.size());
    }

    @Override
    public Page<ContractStatisticsEmpUnitsVO> selectComForRankCountPost(ContractQuery contractQuery, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(contractQuery.getSubjectIds())) {
            return null;
        }
        // 查询所有合同,过滤单位id为空的
        List<ContractStatisticsEmpUnitsVO> contractStatisticsEmpUnitsVOList
                = contractOverviewProviderService.selectComForRankCountList(tokenUser);
        List<ContractStatisticsEmpUnitsVO> entities = contractStatisticsEmpUnitsVOList.stream()
                .filter(contractStatisticsEmpUnitsVO -> StringUtils.isNotBlank(contractStatisticsEmpUnitsVO.getSubjectId()))
                .collect(Collectors.toList());
        // 查询的用工单位id
        List<String> subjectIds = contractQuery.getSubjectIds();
        // 查询所有公司
        List<CompanyDO> companyDOS = companyService.selectByIds(subjectIds, tokenUser.getSiteId());
        Map<String, CompanyDO> idCompanyDOMap = companyDOS.parallelStream()
                .collect(Collectors.toMap(CompanyDO::getId, v -> v, (v1, v2) -> v1));
        // 过滤不存在的用工单位,并写入用工单位名称
        Iterator<ContractStatisticsEmpUnitsVO> iterator = entities.iterator();
        while (iterator.hasNext()) {
            ContractStatisticsEmpUnitsVO next = iterator.next();
            CompanyDO companyDO = idCompanyDOMap.get(next.getSubjectId());
            if (Objects.nonNull(companyDO)) {
                next.setSubjectName(companyDO.getName());
            } else {
                iterator.remove();
            }
        }
        return PageKit.startPage(entities, contractQuery.getPageNo(), contractQuery.getPageSize());
    }


    @Override
    public int getSiteContractLeftCount(String siteId) {
        int count = 0;
        ContractConfigSiteDO contractConfigSiteDO = contractConfigSiteService.selectBySiteId(siteId);
        if (contractConfigSiteDO != null && contractConfigSiteDO.getEnable()) {
            return contractConfigSiteDO.getQuotaRemain();
        }
        return count;
    }

    @Override
    public Page<ContractListVO> selectPageContract(ContractQuery contractQuery, TokenUser tokenUser) {
        Page<ContractListVO> listVOPage = contractOverviewProviderService.selectPageContract(contractQuery,
                tokenUser);
        List<ContractListVO> entities = listVOPage.getEntities();
        // 用工单位idList
        List<String> subjectIdList = entities.stream()
                .map(ContractListVO::getSubjectId)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());
        List<CompanyDO> companyDOList = companyService.selectByIdsContainDelete(subjectIdList, tokenUser.getSiteId());
        Map<String, CompanyDO> idCompanyDOMap = companyDOList.parallelStream()
                .collect(Collectors.toMap(CompanyDO::getId, v -> v));
        for (ContractListVO entity : entities) {
            String subjectId = entity.getSubjectId();
            if (StringUtils.isNotBlank(subjectId)) {
                CompanyDO companyDO = idCompanyDOMap.get(subjectId);
                if (Objects.nonNull(companyDO)) {
                    entity.setSubjectName(companyDO.getName());
                }
            }
        }
        listVOPage.setEntities(entities);
        return listVOPage;
    }

    @Override
    public Page<HrContractListVO> selectPageBySubjectIds(ContractQuery contractQuery, TokenUser tokenUser) {
        Page<ContractListVO> listVOPage = contractOverviewProviderService.selectPageBySubjectIds(contractQuery,
                tokenUser.getSiteId());

        List<HrContractListVO> hrContractListVOList = listToHrContract(listVOPage.getEntities(), tokenUser);
        return new Page<>(hrContractListVOList, listVOPage.getPageNo(), listVOPage.getPageSize(), listVOPage.getEntityCount());
    }


    /**
     * 合同对象 转hr的合同对象
     *
     * @param contractListVOList 合同对象
     * @param tokenUser          登录用户
     * @return hr的合同对象
     */
    private List<HrContractListVO> listToHrContract(List<ContractListVO> contractListVOList, TokenUser tokenUser) {
        if (CollectionUtils.isEmpty(contractListVOList)) {
            return new ArrayList<>();
        }
        // 用工单位idList
        List<String> subjectIdList = contractListVOList.parallelStream().map(ContractListVO::getSubjectId)
                .filter(StringUtils::isNotBlank).collect(Collectors.toList());
        List<CompanyDO> companyDOList = companyService.selectByIdsContainDelete(subjectIdList, tokenUser.getSiteId());
        // 用工单位Id-name
        Map<String, String> companyIdNameMap = companyDOList.parallelStream().collect(Collectors.toMap(CompanyDO::getId, CompanyDO::getName));
        // 用工单位下员工信息
        List<EmpInfoDO> empInfoDOList = empInfoService.selectAllByCompanyIds(new ArrayList<>(companyIdNameMap.keySet())
                , tokenUser.getSiteId());
        // 用户Id-身份证号
        Map<String, String> userIdCardMap = empInfoDOList.parallelStream().sorted(Comparator.comparing(EmpInfoDO::getGmtCreate)
                .reversed()).peek(empInfoDO -> {
            if (empInfoDO.getIdCard() == null) {
                empInfoDO.setIdCard("身份证号未填写");
            }
        }).collect(Collectors.toMap(EmpInfoDO::getUserId, EmpInfoDO::getIdCard, (v1, v2) -> v1));
        // 用户Id查询易签宝账户信息
        List<AccountDO> accountDOList = accountService.findAccounts(AccountType.PERSON
                , new ArrayList<>(userIdCardMap.keySet()));
        // 账户id-userId
        Map<String, String> accountIdMemberIdMap = accountDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, AccountDO::getMemberId));
        // 加入用工单位名称、身份证号、剩余有效时间
        return contractListVOList.parallelStream().map(contractListVO -> {
            String subjectId = contractListVO.getSubjectId();
            // 用工单位名称
            if (StringUtils.isNotBlank(subjectId)) {
                contractListVO.setSubjectName(companyIdNameMap.get(subjectId));
            }
            HrContractListVO hrContractListVO = contractListVO.to(HrContractListVO.class);
            // 放入用工单位
            hrContractListVO.setCompanyName(companyIdNameMap.get(subjectId));
            // 身份证号
            String userId = accountIdMemberIdMap.get(contractListVO.getPartyBAccountId());
            if (userId == null) {
                hrContractListVO.setIdCard("员工已被删除");
            } else {
                String idCard = userIdCardMap.get(userId);
                hrContractListVO.setIdCard(idCard == null ? "身份证号未填写" : idCard);
            }
            // 剩余有效时间
            if (contractListVO.getEndDate() != null) {
                long daysBetween = DateKit8.getDaysBetween(new Date(), contractListVO.getEndDate());
                if (contractListVO.getEndDate().compareTo(new Date()) < 0) {
                    daysBetween *= -1;
                }
                hrContractListVO.setRemainingValidTime(daysBetween);
            }
            return hrContractListVO;
        }).parallel().collect(Collectors.toList());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public List<ContractVO> publishTemplateSign(HrPublishSignDTO hrPublishSignDTO, TokenUser tokenUser) {
        List<String> empIds = hrPublishSignDTO.getMemberVariableList().parallelStream()
                .map(MemberVariableDTO::getPartyBPhone).collect(Collectors.toList());
        ParamException.notEmpty(empIds, "未选择乙方，请重新选择");
        // 使用的模板
        TemplateDO templateDO = getTemplateById(hrPublishSignDTO.getTemplateId(), tokenUser.getSiteId());
        ParamException.notNull(templateDO, "选择的模板不存在，请重新选择");
        // 模板的合同类型
        CategoryVO categoryVO = selectCategoryById(templateDO.getCategoryId(), tokenUser.getSiteId());
        ParamException.notNull(templateDO, "模板合同类型有误，请重新选择");
        // 查询公司下指定员工的电子合同
        List<EmpContractDO> empContractDOList = empContractService
                .selectByCompanyIdAndEmpIds(hrPublishSignDTO.getSubjectId(), empIds);
        // 指定员工的电子合同 按员工Id分组
        Map<String, List<EmpContractDO>> empIdListMap = empContractDOList
                .parallelStream().collect(Collectors.groupingBy(EmpContractDO::getEmpId));
        List<EmpContractAddDTO> empContractAddDTOList = hrPublishSignDTO.getHrMemberVariableDTOList()
                .parallelStream().map(hrMemberVariableDTO -> {
                    // 单个员工的记录
                    List<EmpContractDO> contractDOList = empIdListMap.get(hrMemberVariableDTO.getPartyBEmpId());
                    // 比较新增的电子合同 开始日期 结束日期 合同类型 没有就新建
                    EmpContractDO haveEmpContractDO = null;
                    Date startTime = hrMemberVariableDTO.getStartTime();
                    Date endTime = hrMemberVariableDTO.getEndTime();
                    if (!CollectionUtils.isEmpty(contractDOList)) {
                        for (EmpContractDO empContractDO : contractDOList) {
                            // 类型不同直接排除
                            if (!categoryVO.getName().equals(empContractDO.getType())) {
                                continue;
                            }
                            // 开始时间不同 过滤
                            if (startTime == null || startTime.getTime() != empContractDO.getStartTime().getTime()) {
                                continue;
                            }
                            // 结束时间不同 过滤 有一方不为空 或都不为空 但值不一样
                            if (Objects.equals(endTime, empContractDO.getEndTime())) {
                                continue;
                            }
                            // 查到内容相同的记录
                            haveEmpContractDO = empContractDO;
                        }
                    }
                    if (haveEmpContractDO != null) {
                        return null;
                    }
                    EmpContractAddDTO empContractAddDTO = new EmpContractAddDTO();
                    empContractAddDTO.setEmpId(hrMemberVariableDTO.getPartyBEmpId());
                    empContractAddDTO.setCompanyId(hrPublishSignDTO.getSubjectId());
                    empContractAddDTO.setType(categoryVO.getName());
                    if (startTime == null || endTime == null) {
                        empContractAddDTO.setTermYear(null);
                        empContractAddDTO.setTermMonth(null);
                        empContractAddDTO.setTermDay(null);
                    } else {
                        // 根据合同起始日期计算合同期限
                        Period between = Period.between(startTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
                                , endTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
                        empContractAddDTO.setTermYear(between.getYears());
                        empContractAddDTO.setTermMonth(between.getMonths());
                        empContractAddDTO.setTermDay(between.getDays());
                    }
                    return empContractAddDTO;
                }).filter(Objects::nonNull).collect(Collectors.toList());
        empContractService.batchSave(empContractAddDTOList, tokenUser);
        return signService.publishTemplateSign(hrPublishSignDTO, tokenUser);
    }

    @Override
    public Page<HrContractListVO> selectContractPage(HrContractQuery hrContractQuery, String siteId) {
        // 用工单位下所有员工
        if (StringUtils.isNotBlank(hrContractQuery.getSubjectId())) {
            hrContractQuery.setSubjectIds(Collections.singletonList(hrContractQuery.getSubjectId()));
        }
        List<EmpInfoDO> allEmpInfoDOList = empInfoService.selectByCompanyIdsNotValid(hrContractQuery.getSubjectIds());
        if (StringUtils.isNotBlank(hrContractQuery.getIdCard())) {
            allEmpInfoDOList = allEmpInfoDOList.parallelStream().filter(empInfoDO -> empInfoDO.getIdCard() != null
                    && empInfoDO.getIdCard().contains(hrContractQuery.getIdCard())).collect(Collectors.toList());
        }
        // 按员工状态过滤
        if (hrContractQuery.getEmpStatus() != null) {
            allEmpInfoDOList = allEmpInfoDOList.parallelStream().filter(empInfoDO -> empInfoDO.getStatus() != null
                    && empInfoDO.getStatus().equals(hrContractQuery.getEmpStatus())).collect(Collectors.toList());
        }
        // 查询条件 身份证号非空或状态非空 才按员工过滤过滤
        if (StringUtils.isNotBlank(hrContractQuery.getIdCard()) || hrContractQuery.getEmpStatus() != null) {
            if (CollectionUtils.isEmpty(allEmpInfoDOList)) {
                return new Page<>(hrContractQuery.getPageSize(), hrContractQuery.getPageNo());
            }
            // 用户Ids
            List<String> userIds = allEmpInfoDOList.parallelStream().map(EmpInfoDO::getUserId)
                    .distinct().collect(Collectors.toList());
            hrContractQuery.setMemberIds(userIds);
        }
        // 查询用工单位下所有电子合同信息
        Page<ContractListVO> listVOPage = contractService.selectPageNoSubjectId(hrContractQuery, siteId);
        List<ContractListVO> entities = listVOPage.getEntities();
        if (CollectionUtils.isEmpty(entities)) {
            return new Page<>(hrContractQuery.getPageSize(), hrContractQuery.getPageNo());
        }
        // 用工单位Ids
        List<String> companyIds = entities
                .parallelStream().map(ContractListVO::getSubjectId).collect(Collectors.toList());
        List<CompanyDO> companyDOList = companyService.selectByIdsNotSiteId(companyIds);
        // 用工单位Id-name
        Map<String, String> companyIdNameMap = companyDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, CompanyDO::getName));
        // 涉及的员工 员工的合同账户(目前是易签宝)Id
        Object[] partyBAccountIds = entities.parallelStream()
                .map(ContractListVO::getPartyBAccountId).distinct().toArray(Object[]::new);
        List<AccountDO> accountDOList = accountService.listByIds("default", partyBAccountIds);
        // 账户id-userId
        Map<String, String> accountIdMemberIdMap = accountDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, AccountDO::getMemberId));
        List<EmpInfoDO> empInfoDOList = empInfoService.selectByCompanyIdsAndUserIdsNotValid(companyIds
                , new ArrayList<>(accountIdMemberIdMap.values()));
        // 员工信息按创建时间倒叙
        empInfoDOList = empInfoDOList.parallelStream().sorted(Comparator.comparing(EmpInfoDO::getGmtCreate)
                .reversed()).collect(Collectors.toList());
        // 用工单位Id-用户Id-员工信息
        Map<String, Map<String, EmpInfoDO>> companyIdDOMap = new HashMap<>();
        // 用工单位Id-员工信息列表
        Map<String, List<EmpInfoDO>> companyIdDOListMap = empInfoDOList.stream()
                .collect(Collectors.groupingBy(EmpInfoDO::getCompanyId));
        for (String companyId : companyIdDOListMap.keySet()) {
            // 用工单位下员工
            List<EmpInfoDO> empInfList = companyIdDOListMap.get(companyId);
            Map<String, EmpInfoDO> userIdDOMap = empInfList.stream()
                    .collect(Collectors.toMap(EmpInfoDO::getUserId, v -> v, (v1, v2) -> v1));
            companyIdDOMap.put(companyId, userIdDOMap);
        }
        // 返回结果
        List<HrContractListVO> hrContractListVOList = new ArrayList<>();
        for (ContractListVO contractListVO : entities) {
            HrContractListVO hrContractListVO = contractListVO.to(HrContractListVO.class);
            Map<String, EmpInfoDO> userIdDOMap = companyIdDOMap.get(contractListVO.getSubjectId());
            if (userIdDOMap == null) {
                hrContractListVO.setIdCard("员工已被删除");
            } else {
                String userId = accountIdMemberIdMap.get(contractListVO.getPartyBAccountId());
                if (userId == null) {
                    hrContractListVO.setIdCard("员工已被删除");
                } else {
                    EmpInfoDO empInfoDO = userIdDOMap.get(userId);
                    if (empInfoDO == null) {
                        hrContractListVO.setIdCard("员工已被删除");
                    } else {
                        String idCard = empInfoDO.getIdCard();
                        hrContractListVO.setIdCard(idCard == null ? "身份证号未填写" : idCard);
                    }
                }
            }
            if (contractListVO.getEndDate() != null) {
                // 剩余有效时间
                long daysBetween = DateKit8.getDaysBetween(new Date(), contractListVO.getEndDate());
                if (contractListVO.getEndDate().compareTo(new Date()) < 0) {
                    daysBetween *= -1;
                }
                hrContractListVO.setRemainingValidTime(daysBetween);
            }
            // 放入用工单位名称
            hrContractListVO.setCompanyName(companyIdNameMap.get(contractListVO.getSubjectId()));
            hrContractListVOList.add(hrContractListVO);
        }
        return new Page<>(hrContractListVOList, listVOPage.getPageNo(), listVOPage.getPageSize(), listVOPage.getEntityCount());
    }

    @Override
    public List<AppendixListVO> selectAppendix(AppendixQuery appendixQuery, String siteId) {
        List<AppendixListVO> appendixListVOList = appendixService.select(appendixQuery, siteId);
        if (CollectionUtils.isEmpty(appendixListVOList)) {
            return new ArrayList<>();
        }
        List<Long> contractIds = appendixListVOList.parallelStream()
                .map(AppendixListVO::getContractId).collect(Collectors.toList());
        List<ContractDO> contractDOList = contractService.selectByIds(contractIds);
        // 查询合同信息
        if (CollectionUtils.isEmpty(contractDOList)) {
            return new ArrayList<>();
        }
        Map<String, String> signNoIdCardMap = getSignNoIdCardMap(contractDOList, siteId);
        for (int i = 0; i < appendixListVOList.size(); i++) {
            AppendixListVO appendixListVO = appendixListVOList.get(i);
            String newPath = FileProviderServiceImpl.signNoToIdCard(signNoIdCardMap, appendixListVO.getPath(), i);
            appendixListVO.setPath(newPath);
        }
        return appendixListVOList;
    }

    @Override
    public Result<Boolean> sendContractCode(TokenUser tokenUser) {
        return signService.sendCode(tokenUser);
    }

    @Override
    public List<ContractDO> listAllNoExpireContract() {
        return contractService.listAllNoExpireContract();
    }

    @Override
    public ContractDO selectById(Long id) {
        List<ContractDO> contractDOS = contractService.selectByIds(Collections.singletonList(id));
        if (CollectionUtils.isEmpty(contractDOS)) {
            return null;
        }
        return contractDOS.get(0);
    }

    @Override
    public List<SealDO> selectBySubjectId(String subjectId, String siteId) {
        return sealService.selectBySubjectId(subjectId, siteId);
    }

    @Override
    public List<SealVO> saveIssuedSeal(IssuedSealAddDTO issuedSealAddDTO, TokenUser tokenUser) {
        return sealService.saveIssuedSeal(issuedSealAddDTO, tokenUser);
    }

    @Override
    public List<FailCategoryVO> saveIssuedCategory(IssuedCategoryAddDTO issuedCategoryAddDTO, TokenUser tokenUser) {
        // 下发失败列表
        List<FailCategoryVO> failCategoryVOList = new ArrayList<>();
        // 用工单位Id-name
        Map<String, String> subjectIdNameMap = issuedCategoryAddDTO.getSubjectList().parallelStream().collect(Collectors
                .toMap(IssuedCategoryAddDTO.Subject::getSubjectId, IssuedCategoryAddDTO.Subject::getSubjectName, (v1, v2) -> v1));
        // 用工单位Ids
        List<String> companyIds = new ArrayList<>(subjectIdNameMap.keySet());
        // 用工单位下正在使用的合同类型
        List<Long> usedCategoryIds = contractService.selectCategoryIdBySubjectIds(companyIds);
        // 租户级合同类型Id-合同分组名称
        Map<Long, String> categoryIdGroupNameMap = new HashMap<>(16);
        // 全部合同类型Ids
        List<Long> allCategoryIds = new ArrayList<>();
        for (IssuedCategoryAddDTO.Category category : issuedCategoryAddDTO.getCategoryList()) {
            for (Long categoryId : category.getCategoryIds()) {
                categoryIdGroupNameMap.put(categoryId, category.getContractGroupName());
                allCategoryIds.add(categoryId);
            }
        }
        // 租户全部合同类型
        List<CategoryDO> categoryDOList = categoryService.selectByIds(allCategoryIds);
        if (CollectionUtils.isEmpty(categoryDOList)) {
            return new ArrayList<>();
        }
        // 用工单位下同名合同类型
        List<CategoryDO> hadCategoryList = categoryService.selectBySubjectIdsAndNames(companyIds
                , categoryDOList.parallelStream().map(CategoryDO::getName).collect(Collectors.toList()), tokenUser);
        // 用工单位Id-同名合同类型
        Map<String, List<CategoryDO>> hadNameMap = hadCategoryList.parallelStream()
                .collect(Collectors.groupingBy(CategoryDO::getSubjectId));
        // 是否同步下发印章
        boolean isSeal = issuedCategoryAddDTO.getIsSeal() != null && issuedCategoryAddDTO.getIsSeal();
        // 租户合同类型使用的印章
        List<SealDO> sealDOList = new ArrayList<>();
        // 用工单位Id-同名印章
        Map<String, List<SealDO>> hadSealListMap = new HashMap<>(16);
        if (isSeal) {
            // 下发的租户合同类型所带印章
            List<Long> sealIds = categoryDOList.parallelStream()
                    .map(CategoryDO::getEnterpriseSealId).distinct().collect(Collectors.toList());
            sealDOList = sealService.selectByIds(sealIds, tokenUser.getSiteId());
            List<String> sealNames = sealDOList.parallelStream().map(SealDO::getName).collect(Collectors.toList());
            // 用工单位同名印章的合同类型
            List<SealDO> hadSealDOList = sealService.selectBySubjectIdsAndNames(companyIds, sealNames, tokenUser);
            hadSealListMap = hadSealDOList.parallelStream().collect(Collectors.groupingBy(SealDO::getSubjectId));
        }
        for (String companyId : companyIds) {
            // 用工单位名称
            FailCategoryVO failCategoryVO = new FailCategoryVO();
            failCategoryVO.setCompanyId(companyId);
            failCategoryVO.setCompanyName(subjectIdNameMap.get(companyId));
            // 下发失败的合同类型
            List<String> failCategoryList = new ArrayList<>();
            failCategoryVO.setFailCategoryList(failCategoryList);
            failCategoryVOList.add(failCategoryVO);

            // 1、下发分组
            // 需要下发的合同分组
            List<String> groupNames = issuedCategoryAddDTO.getCategoryList().parallelStream()
                    .map(IssuedCategoryAddDTO.Category::getContractGroupName).distinct().collect(Collectors.toList());
            // 用工单位的模板分组名称-id
            Map<String, Long> groupNameIdMap = issuedGroup(companyId, groupNames, tokenUser);

            // 2、下发印章
            // 租户印章Id-用工单位印章Id
            Map<Long, Long> sealIdMap = new HashMap<>(16);
            if (isSeal) {
                // 用工单位同名印章
                List<SealDO> hadSealDOList = hadSealListMap.get(companyId);
                sealIdMap = issuedSeal(companyId, sealDOList, hadSealDOList, tokenUser);
            }

            // 3、下发合同类型
            // 同名合同类型
            List<CategoryDO> hadNameList = hadNameMap.get(companyId);
            // 租户合同类型Id-用工单位合同类型Id
            Map<Long, Long> categoryIdMap = issuedCategory(companyId, categoryDOList, hadNameList, categoryIdGroupNameMap
                    , groupNameIdMap, sealIdMap, isSeal, usedCategoryIds, failCategoryList, tokenUser);

            // 4、下发模板
            Map<Long, Long> templateIdMap = issuedTemplate(categoryIdMap, tokenUser);

            // 5、下发模板变量
            issuedTemplateVariable(companyId, templateIdMap, tokenUser);

            // 6、下发租户必填信息变量
            issuedNecessaryVariable(companyId, templateIdMap, tokenUser);

            // 6、下发自定义信息变量
            issuedAdditionalVariable(companyId, templateIdMap, tokenUser);

            // 7、下发印章位置
            issuedSealPosition(templateIdMap, tokenUser);
        }
        return failCategoryVOList;
    }

    /**
     * 下发分组
     *
     * @param companyId  用工单位Id
     * @param groupNames 合同所在分组名称
     * @param tokenUser  登录用户
     * @return 用工单位的模板分组名称-id
     */
    private Map<String, Long> issuedGroup(String companyId, List<String> groupNames, TokenUser tokenUser) {
        // 用工单位下合同类型分组
        List<ContractGroupDO> contractGroupDOList = contractGroupService.selectBySubjectId(companyId, tokenUser);
        // 用工单位模板分组名称-id
        Map<String, Long> groupNameIdMap = contractGroupDOList.parallelStream()
                .collect(Collectors.toMap(ContractGroupDO::getName, BaseDO::getId, (v1, v2) -> v1));
        for (String groupName : groupNames) {
            Long groupId = groupNameIdMap.get(groupName);
            // 用工单位下没有分组 需要新建
            if (groupId == null) {
                ContractGroupAddDTO contractGroupAddDTO = new ContractGroupAddDTO();
                contractGroupAddDTO.setName(groupName);
                contractGroupAddDTO.setSubjectId(companyId);
                ContractGroupVO contractGroupVO = contractGroupService.save(contractGroupAddDTO, tokenUser);
                groupNameIdMap.put(groupName, contractGroupVO.getId());
            }
        }
        return groupNameIdMap;
    }

    /**
     * 下发印章
     *
     * @param companyId     用工单位Id
     * @param sealDOList    待下发的印章Id
     * @param hadSealDOList 已存在的同名印章Id
     * @param tokenUser     登录用户
     * @return 租户印章Id-用工单位印章Id
     */
    private Map<Long, Long> issuedSeal(String companyId, List<SealDO> sealDOList, List<SealDO> hadSealDOList, TokenUser tokenUser) {
        Map<Long, Long> sealIdMap = new HashMap<>(16);
        // 用工单位印章名称-id
        Map<String, Long> sealNameId = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(hadSealDOList)) {
            sealNameId = hadSealDOList.parallelStream().collect(Collectors.toMap(SealDO::getName, BaseDO::getId, (v1, v2) -> v1));
        }
        // 需要下发的印章
        for (SealDO sealDO : sealDOList) {
            Long sealId = sealNameId.get(sealDO.getName());
            if (sealId == null) {
                // 用工单位下无同名印章 新增
                SealAddDTO sealAddDTO = sealDO.to(SealAddDTO.class);
                sealAddDTO.setSubjectId(companyId);
                sealAddDTO.setIssue(true);
                sealAddDTO.setSourceId(sealDO.getId() + "");
                SealVO save = sealService.save(sealAddDTO, tokenUser);
                sealIdMap.put(sealDO.getId(), save.getId());
            } else {
                // 用工单位下有同名印章 更新
                SealUpdateDTO sealUpdateDTO = sealDO.to(SealUpdateDTO.class);
                sealUpdateDTO.setId(sealId);
                sealUpdateDTO.setSubjectId(companyId);
                sealUpdateDTO.setIssue(true);
                sealUpdateDTO.setSourceId(sealDO.getId() + "");
                sealService.update(sealUpdateDTO, tokenUser);
                sealIdMap.put(sealDO.getId(), sealId);
            }
        }
        return sealIdMap;
    }

    /**
     * 下发合同类型
     *
     * @param companyId              用工单位Id
     * @param categoryDOList         租户全部合同类型
     * @param hadNameList            同名合同类型
     * @param categoryIdGroupNameMap 租户级合同类型Id-合同分组名称
     * @param groupNameIdMap         用工单位的模板分组名称-id
     * @param sealIdMap              租户印章Id-用工单位印章Id
     * @param isSeal                 是否同步下发印章
     * @param usedCategoryIds        用工单位下正在使用的合同类型
     * @param failCategoryList       下发失败的合同类型
     * @param tokenUser              登录用户
     * @return 租户合同类型Id-用工单位合同类型Id
     */
    private Map<Long, Long> issuedCategory(String companyId, List<CategoryDO> categoryDOList, List<CategoryDO> hadNameList
            , Map<Long, String> categoryIdGroupNameMap, Map<String, Long> groupNameIdMap, Map<Long, Long> sealIdMap
            , boolean isSeal, List<Long> usedCategoryIds, List<String> failCategoryList, TokenUser tokenUser) {

        // 同名合同类型名称-DO
        Map<String, CategoryDO> categoryNameDOMap = new HashMap<>(16);
        if (!CollectionUtils.isEmpty(hadNameList)) {
            categoryNameDOMap = hadNameList.parallelStream().collect(Collectors.toMap(CategoryDO::getName, v -> v, (v1, v2) -> v1));
        }
        // 租户合同类型Id-用工单位合同类型Id
        Map<Long, Long> categoryIdMap = new HashMap<>(16);
        for (CategoryDO categoryDO : categoryDOList) {
            // 分组Id
            Long groupId = groupNameIdMap.get(categoryIdGroupNameMap.get(categoryDO.getId()));
            // 印章Id
            Long sealId = null;
            // 只有选择同步下发印章 才放入印章Id
            if (isSeal) {
                sealId = sealIdMap.get(categoryDO.getEnterpriseSealId());
            }
            CategoryDO hadDO = categoryNameDOMap.get(categoryDO.getName());
            if (hadDO == null) {
                // 没有同名 新建
                CategoryAddDTO categoryAddDTO = categoryDO.to(CategoryAddDTO.class);
                categoryAddDTO.setSubjectId(companyId);
                categoryAddDTO.setGroupId(groupId);
                categoryAddDTO.setEnterpriseSealId(sealId);
                categoryAddDTO.setIssue(Boolean.TRUE);
                categoryAddDTO.setSourceId(categoryDO.getId() + "");
                CategoryVO categoryVO = categoryService.saveNoLogic(categoryAddDTO, tokenUser);
                categoryIdMap.put(categoryDO.getId(), categoryVO.getId());
            } else {
                // 有同名
                if (usedCategoryIds.contains(hadDO.getId())) {
                    // 同名类型正在使用 跳过
                    failCategoryList.add(categoryDO.getName());
                    continue;
                } else {
                    // 同名类型未使用 更新
                    CategoryUpdateDTO categoryUpdateDTO = categoryDO.to(CategoryUpdateDTO.class);
                    categoryUpdateDTO.setId(hadDO.getId());
                    categoryUpdateDTO.setGroupId(groupId);
                    categoryUpdateDTO.setEnterpriseSealId(sealId);
                    categoryUpdateDTO.setSubjectId(companyId);
                    categoryUpdateDTO.setIssue(Boolean.TRUE);
                    categoryUpdateDTO.setSourceId(categoryDO.getId() + "");
                    categoryService.update(categoryUpdateDTO, tokenUser);
                }
                categoryIdMap.put(categoryDO.getId(), hadDO.getId());
            }
        }
        return categoryIdMap;
    }

    /**
     * 下发模板
     *
     * @param categoryIdMap 租户合同类型Id-用工单位合同类型Id
     * @param tokenUser     登录用户
     * @return 租户合同模板Id-用工单位合同模板Id
     */
    private Map<Long, Long> issuedTemplate(Map<Long, Long> categoryIdMap, TokenUser tokenUser) {
        // 删除用工单位原有合同类型下模板
        List<TemplateDO> templateList = templateService.selectByCategoryIds(
                new ArrayList<>(categoryIdMap.values()), tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(templateList)) {
            templateService.deleteByIds(tokenUser, templateList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        // 下发成功的租户合同类型对应模板
        List<TemplateDO> templateDOList = templateService.selectByCategoryIds(
                new ArrayList<>(categoryIdMap.keySet()), tokenUser.getSiteId());
        // 租户合同模板Id-用工单位合同模板Id
        Map<Long, Long> templateIdMap = new HashMap<>(16);
        for (TemplateDO templateDO : templateDOList) {
            // 租户下模板Id
            Long templateId = templateDO.getId();
            templateDO.setId(null);
            templateDO.setCategoryId(categoryIdMap.get(templateDO.getCategoryId()));
            // 新建的模板
            TemplateDO newTemplateDO = templateService.save(templateDO, tokenUser.getSiteId(), tokenUser.getSiteId());
            templateIdMap.put(templateId, newTemplateDO.getId());
        }
        return templateIdMap;
    }

    /**
     * 下发模板变量
     *
     * @param companyId     用工单位Id
     * @param templateIdMap 租户合同模板Id-用工单位合同模板Id
     * @param tokenUser     登录用户
     */
    private void issuedTemplateVariable(String companyId, Map<Long, Long> templateIdMap, TokenUser tokenUser) {
        // 删除用工单位原有合同类型下模板变量
        List<TemplateVariableDO> templateVariableList = templateVariableService
                .selectByTemplateIds(new ArrayList<>(templateIdMap.values()), tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(templateVariableList)) {
            templateVariableService.deleteByIds(tokenUser
                    , templateVariableList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        // 下发成功的租户合同类型对应模板变量
        List<TemplateVariableDO> templateVariableDOList = templateVariableService
                .selectByTemplateIds(new ArrayList<>(templateIdMap.keySet()), tokenUser.getSiteId());
        // 创建用工单位模板变量
        List<TemplateVariableAddDTO> templateVariableAddDTOList = new ArrayList<>();
        for (TemplateVariableDO templateVariableDO : templateVariableDOList) {
            TemplateVariableAddDTO templateVariableAddDTO
                    = templateVariableDO.to(TemplateVariableAddDTO.class);
            templateVariableAddDTO.setTemplateId(templateIdMap.get(templateVariableDO.getTemplateId()));
            templateVariableAddDTOList.add(templateVariableAddDTO);
        }
        templateVariableService.batchSave(templateVariableAddDTOList, tokenUser);
    }

    /**
     * 下发系统默认信息变量
     *
     * @param companyId     用工单位Id
     * @param templateIdMap 租户合同模板Id-用工单位合同模板Id
     * @param tokenUser     登录用户
     */
    private void issuedNecessaryVariable(String companyId, Map<Long, Long> templateIdMap, TokenUser tokenUser) {
        // 删除用工单位原有合同类型下默认信息变量
        List<NecessaryVariableDO> necessaryVariableDOList = necessaryVariableService.selectByTemplateIds(
                new ArrayList<>(templateIdMap.values()), tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(necessaryVariableDOList)) {
            necessaryVariableService.deleteByIds(tokenUser
                    , necessaryVariableDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        // 租户默认信息变量
        List<NecessaryVariableDO> siteNecessaryVariableDOList = necessaryVariableService.selectByTemplateIds(
                new ArrayList<>(templateIdMap.keySet()), tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(siteNecessaryVariableDOList)) {
            return;
        }
        List<CompanyNecessaryFieldDO> siteNecessaryFieldDOList = companyNecessaryFieldService
                .selectByCompanyId("site", tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(siteNecessaryFieldDOList)) {
            return;
        }
        Map<String, String> idFieldNameMap = siteNecessaryFieldDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, CompanyNecessaryFieldDO::getFieldName));

        // 用工单位花名册固定(必填)字段
        List<CompanyNecessaryFieldDO> companyNecessaryFieldDOList = companyNecessaryFieldService
                .selectByCompanyId(companyId, tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(companyNecessaryFieldDOList)) {
            return;
        }
        Map<String, String> fieldNameIdMap = companyNecessaryFieldDOList.parallelStream()
                .collect(Collectors.toMap(CompanyNecessaryFieldDO::getFieldName, BaseDO::getId));
        List<NecessaryVariableAddDTO> necessaryVariableAddDTOList = new ArrayList<>();
        for (NecessaryVariableDO necessaryVariableDO : siteNecessaryVariableDOList) {
            // 租户级默认信息名称
            String fieldName = idFieldNameMap.get(necessaryVariableDO.getNecessaryId());
            // 用工单位级默认信息Id
            String necessaryId = fieldNameIdMap.get(fieldName);
            NecessaryVariableAddDTO necessaryVariableAddDTO = new NecessaryVariableAddDTO();
            necessaryVariableAddDTO.setTemplateId(templateIdMap.get(necessaryVariableDO.getTemplateId()));
            necessaryVariableAddDTO.setNecessaryId(necessaryId);
            necessaryVariableAddDTOList.add(necessaryVariableAddDTO);
        }
        necessaryVariableService.batchSave(necessaryVariableAddDTOList, tokenUser);
    }

    private void issuedAdditionalVariable(String companyId, Map<Long, Long> templateIdMap, TokenUser tokenUser) {
        // 租户员工自定义
        List<AdditionalVariableDO> additionalVariableDOList =
                additionalVariableService.selectByTemplateIds(new ArrayList<>(templateIdMap.keySet()), tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(additionalVariableDOList)) {
            return;
        }
        // 租户自定义字段信息
        List<CompanyAdditionalFieldsDO> companyAdditionalFieldsDOList = companyAdditionalFieldsService
                .selectDOByIds(additionalVariableDOList.parallelStream().map(AdditionalVariableDO::getAdditionalId)
                        .collect(Collectors.toList()), tokenUser.getSiteId());
        // 租户自定义字段id-名称
        Map<Long, String> idFieldNameMap = companyAdditionalFieldsDOList.parallelStream()
                .collect(Collectors.toMap(BaseDO::getId, CompanyAdditionalFieldsDO::getFieldName));
        // 用工单位自定义字段信息
        List<CompanyAdditionalFieldsListVO> companyAdditionalFieldsListVOList = companyAdditionalFieldsService
                .selectByCompanyId2(companyId, null, tokenUser.getSiteId());
        // 租户自定义字段id-名称
        Map<String, Long> fieldNameIdMap = companyAdditionalFieldsListVOList.parallelStream()
                .collect(Collectors.toMap(CompanyAdditionalFieldsListVO::getFieldName
                        , CompanyAdditionalFieldsListVO::getId, (v1, v2) -> v1));
        List<AdditionalVariableAddDTO> additionalVariableAddDTOList = new ArrayList<>();
        for (AdditionalVariableDO additionalVariableDO : additionalVariableDOList) {
            // 字段名称
            String fieldName = idFieldNameMap.get(additionalVariableDO.getAdditionalId());
            if (fieldName == null) {
                continue;
            }
            Long id = fieldNameIdMap.get(fieldName);
            if (id == null) {
                continue;
            }
            AdditionalVariableAddDTO additionalVariableAddDTO
                    = additionalVariableDO.to(AdditionalVariableAddDTO.class);
            additionalVariableAddDTO.setTemplateId(templateIdMap.get(additionalVariableDO.getTemplateId()));
            additionalVariableAddDTO.setAdditionalId(id);
            additionalVariableAddDTOList.add(additionalVariableAddDTO);
        }
        additionalVariableService.batchSave(additionalVariableAddDTOList, tokenUser);
    }

    /**
     * 下发印章位置
     *
     * @param templateIdMap 租户合同模板Id-用工单位合同模板Id
     * @param tokenUser     登录用户
     */
    private void issuedSealPosition(Map<Long, Long> templateIdMap, TokenUser tokenUser) {
        // 删除用工单位原有合同类型下印章位置
        List<SealPositionDO> sealPositionList = sealPositionService
                .selectByTemplateIds(new ArrayList<>(templateIdMap.values()), tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(sealPositionList)) {
            sealService.deleteByIds(tokenUser
                    , sealPositionList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        // 下发成功的租户合同类型对应模板变量
        List<SealPositionDO> sealPositionDOList = sealPositionService
                .selectByTemplateIds(new ArrayList<>(templateIdMap.keySet()), tokenUser.getSiteId());
        // 创建用工单位印章位置
        List<SealPositionAddDTO> sealPositionAddDTOList = new ArrayList<>();
        for (SealPositionDO sealPositionDO : sealPositionDOList) {
            SealPositionAddDTO sealPositionAddDTO = sealPositionDO.to(SealPositionAddDTO.class);
            sealPositionAddDTO.setTemplateId(templateIdMap.get(sealPositionDO.getTemplateId()));
            sealPositionAddDTOList.add(sealPositionAddDTO);
        }
        sealPositionService.batchSave(sealPositionAddDTOList, tokenUser);
    }

    @Override
    public void issuedDefaultVariable(TokenUser tokenUser) {
        // 租户级合同类型
        List<CategoryDO> categoryDOList = categoryService.listBySubjectId("0", tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(categoryDOList)) {
            log.error("租户：{}级没有合同模板", tokenUser.getSiteId());
            return;
        }
        // 租户级合同模板
        List<TemplateDO> templateDOList = templateService.selectByCategoryIds(categoryDOList.parallelStream()
                .map(BaseDO::getId).collect(Collectors.toList()), tokenUser.getSiteId());
        List<Long> templateIds = templateDOList.parallelStream().map(BaseDO::getId).collect(Collectors.toList());
        // 系统模板变量
        List<DefaultVariableDO> defaultVariableDOList = adminProviderService.selectDefaultVariableByTemplateIds(templateIds);
        if (CollectionUtils.isEmpty(defaultVariableDOList)) {
            return;
        }
        int saveCount = savesiteNecessaryVariable(templateIds, defaultVariableDOList, tokenUser);

        log.info("租户：{}，同步了：{}个默认字段到必填字段", tokenUser.getSiteId(), saveCount);
    }

    /**
     * 合同模板关联默认字段转化为必填字段
     *
     * @param templateIds           需要修改的模板Id
     * @param defaultVariableDOList 需要修改的模板项
     * @param tokenUser             登录用户
     * @return 保存条数
     */
    private int savesiteNecessaryVariable(List<Long> templateIds, List<DefaultVariableDO> defaultVariableDOList, TokenUser tokenUser) {
        List<String> defaultIds = defaultVariableDOList.parallelStream()
                .map(DefaultVariableDO::getDefaultId).distinct().collect(Collectors.toList());
        List<DefaultFieldDO> defaultFieldDOList = adminProviderService.selectDefaultFieldByIdsNotSiteId(defaultIds);
        if (CollectionUtils.isEmpty(defaultFieldDOList)) {
            return 0;
        }
        // 系统默认信息Id-内容
        Map<String, DefaultFieldDO> defaultFieldDOMap = defaultFieldDOList
                .parallelStream().collect(Collectors.toMap(BaseDO::getId, v -> v));
        // 租户级花名册必填字段
        List<CompanyNecessaryFieldDO> siteNecessaryFieldDOList = companyNecessaryFieldService
                .selectByCompanyId("site", tokenUser.getSiteId());
        if (CollectionUtils.isEmpty(siteNecessaryFieldDOList)) {
            return 0;
        }
        Map<String, String> fieldNameIdMap = siteNecessaryFieldDOList.parallelStream()
                .collect(Collectors.toMap(CompanyNecessaryFieldDO::getFieldName, BaseDO::getId));
        // 原有合同类型下默认信息变量
        List<NecessaryVariableDO> necessaryVariableDOList = necessaryVariableService.selectByTemplateIds(
                new ArrayList<>(templateIds), tokenUser.getSiteId());
        Map<Long, List<NecessaryVariableDO>> templateIdListMap = necessaryVariableDOList.parallelStream()
                .collect(Collectors.groupingBy(NecessaryVariableDO::getTemplateId));
        List<NecessaryVariableAddDTO> necessaryVariableAddDTOList = new ArrayList<>();
        for (DefaultVariableDO defaultVariableDO : defaultVariableDOList) {
            Long templateId = defaultVariableDO.getTemplateId();
            DefaultFieldDO defaultFieldDO = defaultFieldDOMap.get(defaultVariableDO.getDefaultId());
            if (defaultFieldDO == null) {
                continue;
            }
            if ("empFamily".equals(defaultFieldDO.getModuleCode())) {
                switch (defaultFieldDO.getFieldCode()) {
                    case "name":
                        defaultFieldDO.setFieldName("成员姓名(家庭成员)");
                        break;
                    case "relation":
                        defaultFieldDO.setFieldName("与我的关系(家庭成员)");
                        break;
                    case "phone":
                        defaultFieldDO.setFieldName("联系电话(家庭成员)");
                        break;
                    case "workUnit":
                        defaultFieldDO.setFieldName("工作单位(家庭成员)");
                        break;
                }
            } else if ("empContacts".equals(defaultFieldDO.getModuleCode())) {
                switch (defaultFieldDO.getFieldCode()) {
                    case "name":
                        defaultFieldDO.setFieldName("联系人姓名(紧急联系人)");
                        break;
                    case "relation":
                        defaultFieldDO.setFieldName("与我的关系(紧急联系人)");
                        break;
                    case "phone":
                        defaultFieldDO.setFieldName("联系电话(紧急联系人)");
                        break;
                    case "address":
                        defaultFieldDO.setFieldName("联系人地址(紧急联系人)");
                        break;
                }
            }
            // 必填字段Id
            String necessaryFieldId = fieldNameIdMap.get(defaultFieldDO.getFieldName());
            if (necessaryFieldId == null) {
                continue;
            }
            // 已存在的变量信息
            List<NecessaryVariableDO> necessaryVariableList = templateIdListMap.get(templateId);
            if (!CollectionUtils.isEmpty(necessaryVariableList)) {
                List<String> necessaryIds = necessaryVariableList.parallelStream()
                        .map(NecessaryVariableDO::getNecessaryId).collect(Collectors.toList());
                if (necessaryIds.contains(necessaryFieldId)) {
                    continue;
                }
            }
            NecessaryVariableAddDTO necessaryVariableAddDTO
                    = defaultVariableDO.to(NecessaryVariableAddDTO.class);
            necessaryVariableAddDTO.setTemplateId(templateId);
            necessaryVariableAddDTO.setNecessaryId(necessaryFieldId);
            necessaryVariableAddDTOList.add(necessaryVariableAddDTO);
        }
        necessaryVariableService.batchSave(necessaryVariableAddDTOList, tokenUser);
        return necessaryVariableAddDTOList.size();
    }


    @Override
    public void repairTemplateVariable() {
        // 全部合同类型
        Example categoryExample = new Example(CategoryDO.class);
        categoryExample.and().andEqualTo("valid", true);
        List<CategoryDO> categoryDOList = categoryDAO.selectByExample(categoryExample);
        if (CollectionUtils.isEmpty(categoryDOList)) {
            return;
        }
        // 合同类型-用工单位
        Map<Long, String> categoryIdCompanyIdMap = categoryDOList.parallelStream().collect(Collectors
                .toMap(BaseDO::getId, CategoryDO::getSubjectId));
        // 全部合同模板
        Example templateExample = new Example(TemplateDO.class);
        templateExample.and().andIn("categoryId", categoryDOList.parallelStream().map(BaseDO::getId).collect(Collectors.toList()));
        List<TemplateDO> templateDOList = templateDAO.selectByExample(templateExample);
        // 用工单位-合同模板Map
        Map<String, List<HrTemplateDTO>> companyIdTemplateMap = templateDOList.parallelStream().map(templateDO -> {
            HrTemplateDTO hrTemplateDTO = templateDO.to(HrTemplateDTO.class);
            String companyId = categoryIdCompanyIdMap.get(templateDO.getCategoryId());
            if (companyId == null) {
                return null;
            }
            hrTemplateDTO.setCompanyId(companyId);
            return hrTemplateDTO;
        }).filter(Objects::nonNull).collect(Collectors.groupingBy(HrTemplateDTO::getCompanyId));
        // 全部用工单位
        List<CompanyDO> companyDOList = companyService.selectAll();
//        CompanyDO company = companyService.selectDOByIdNotSite("DAFDB8EF69F7460295C9EA85A5A470B8");
//        List<CompanyDO> companyDOList = Collections.singletonList(company);
        for (CompanyDO companyDO : companyDOList) {
            TokenUser tokenUser = new TokenUser("repairTemplateVariable", companyDO.getSiteId());
            // 用工单位Id
            String companyId = companyDO.getId();

            // 用工单位下的合同变量
            List<HrVariableListVO> hrVariableListVOList = selectVariableNotPersonal(companyId, null, tokenUser);

            List<HrTemplateDTO> hrTemplateDTOList = companyIdTemplateMap.get(companyId);
            // 用工单位下的合同模板
            if (CollectionUtils.isEmpty(hrTemplateDTOList)) {
                continue;
            }
            for (HrTemplateDTO hrTemplateDTO : hrTemplateDTOList) {
                // 模板内容
                String content = hrTemplateDTO.getContent();

                // 合同原有变量Ids
                List<Long> variableIds = new ArrayList<>();
                // 员工必填信息Ids
                List<String> necessaryIds = new ArrayList<>();
                // 员工自定义信息Ids
                List<Long> additionalIds = new ArrayList<>();

                for (HrVariableListVO hrVariableListVO : hrVariableListVOList) {
                    if (StringUtils.isBlank(content)) {
                        continue;
                    }
                    if (content.contains("#" + hrVariableListVO.getName() + "#")) {
                        if (hrVariableListVO.getId() != null) {
                            // 公司或合同相关
                            variableIds.add(hrVariableListVO.getId());
                        } else if (StringUtils.isNotBlank(hrVariableListVO.getNecessaryId())) {
                            // 花名册自定义字段
                            necessaryIds.add(hrVariableListVO.getNecessaryId());
                        } else if (hrVariableListVO.getAdditionalId() != null) {
                            additionalIds.add(hrVariableListVO.getAdditionalId());
                        }
                    }
                }
                TemplateVariableUpdateDTO templateVariableUpdateDTO = new TemplateVariableUpdateDTO();
                templateVariableUpdateDTO.setTemplateId(hrTemplateDTO.getId());
                templateVariableUpdateDTO.setVariableIds(variableIds);
                templateVariableUpdateDTO.setNecessaryIds(necessaryIds);
                templateVariableUpdateDTO.setAdditionalIds(additionalIds);
                updateTemplateVariable(templateVariableUpdateDTO, tokenUser);
            }
        }
    }

    @Override
    public List<HrVariableListVO> selectVariableNotPersonal(String companyId, String fieldName, TokenUser tokenUser) {
        List<HrVariableListVO> variableListVOList = new ArrayList<>();
        List<VariableDO> variableDOList = variableService.selectAll();
        // 必填字段模块
        List<String> personalInfoModuleCodeList = new ArrayList<>();
        //员工基本信息
        personalInfoModuleCodeList.add("basic");
        //员工联系人
        personalInfoModuleCodeList.add("contact");
        //员工紧急联系人
        personalInfoModuleCodeList.add("empContacts");
        //员工家庭成员
        personalInfoModuleCodeList.add("empFamily");
        // 员工教育经历
        personalInfoModuleCodeList.add("empEducation");
        // 公司员工必填字段
        List<CompanyNecessaryFieldDO> companyNecessaryFieldDOList = companyNecessaryFieldService
                .selectByCompanyAndModuleCodes(companyId, personalInfoModuleCodeList, tokenUser.getSiteId());
        for (CompanyNecessaryFieldDO companyNecessaryFieldDO : companyNecessaryFieldDOList) {
            HrVariableListVO hrVariableListVO = new HrVariableListVO();
            hrVariableListVO.setNecessaryId(companyNecessaryFieldDO.getId());
            hrVariableListVO.setType(companyNecessaryFieldDO.getModuleCode());
            if ("basic".equals(companyNecessaryFieldDO.getModuleCode()) && "姓名".equals(companyNecessaryFieldDO.getFieldName())) {
                hrVariableListVO.setName("员工姓名");
            } else if ("empFamily".equals(companyNecessaryFieldDO.getModuleCode())) {
                if (!companyNecessaryFieldDO.getFieldName().contains("(家庭成员)")) {
                    hrVariableListVO.setName(companyNecessaryFieldDO.getFieldName() + "(家庭成员)");
                } else {
                    hrVariableListVO.setName(companyNecessaryFieldDO.getFieldName());
                }
            } else if ("empContacts".equals(companyNecessaryFieldDO.getModuleCode())) {
                if (!companyNecessaryFieldDO.getFieldName().contains("(紧急联系人)")) {
                    hrVariableListVO.setName(companyNecessaryFieldDO.getFieldName() + "(紧急联系人)");
                } else {
                    hrVariableListVO.setName(companyNecessaryFieldDO.getFieldName());
                }
            } else {
                hrVariableListVO.setName(companyNecessaryFieldDO.getFieldName());
            }
            hrVariableListVO.setNeed(companyNecessaryFieldDO.getIsNecessary());
            variableListVOList.add(hrVariableListVO);
        }

        // ID1000519：额外字段加入在职信息
        personalInfoModuleCodeList = new ArrayList<>();
        //员工基本信息
        personalInfoModuleCodeList.add("basic");
        //员工联系人
        personalInfoModuleCodeList.add("contact");
        // 员工状态
        personalInfoModuleCodeList.add("empState");
        // 在职信息
        personalInfoModuleCodeList.add("onJob");
        // 档案信息
        personalInfoModuleCodeList.add("file");
        // 退休信息
        personalInfoModuleCodeList.add("retire");
        List<CompanyAdditionalFieldsDO> companyAdditionalFieldsDOList;
        // 租户级自定义字段去所有用工单位合集(逻辑同入职模板)
        if ("site".equals(companyId)) {
            List<CompanyAdditionalFieldsListVO> companyAdditionalFieldsListVOList = companyAdditionalFieldsService
                    .selectByCompanyId2(companyId, fieldName, tokenUser.getSiteId());
            companyAdditionalFieldsDOList = companyAdditionalFieldsListVOList.parallelStream()
                    .map(companyAdditionalFieldsListVO -> companyAdditionalFieldsListVO.to(CompanyAdditionalFieldsDO.class))
                    .collect(Collectors.toList());
        } else {
            companyAdditionalFieldsDOList = companyAdditionalFieldsService
                    .selectByCompanyAndModuleCodes(companyId, personalInfoModuleCodeList, tokenUser.getSiteId());
        }
        // 公司员工额外字段
        Map<String, Integer> fieldNameSort = new HashMap<>();
        fieldNameSort.put("basic", 1);
        fieldNameSort.put("contact", 2);
        fieldNameSort.put("empState", 3);
        fieldNameSort.put("onJob", 4);
        fieldNameSort.put("file", 5);
        fieldNameSort.put("retire", 6);
        fieldNameSort.put("annex", 7);
        fieldNameSort.put("archive", 8);
        fieldNameSort.put("leaveOffice", 9);
        // 额外字段按模块排序
        companyAdditionalFieldsDOList = companyAdditionalFieldsDOList.parallelStream().sorted(Comparator.comparing(
                        companyAdditionalFieldsDO -> fieldNameSort.get(companyAdditionalFieldsDO.getModuleCode())))
                .parallel().collect(Collectors.toList());
        for (CompanyAdditionalFieldsDO companyAdditionalFieldsDO : companyAdditionalFieldsDOList) {
            HrVariableListVO hrVariableListVO = new HrVariableListVO();
            hrVariableListVO.setAdditionalId(companyAdditionalFieldsDO.getId());
            if ("basic".equals(companyAdditionalFieldsDO.getModuleCode())) {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(基本信息)");
            } else if ("contact".equals(companyAdditionalFieldsDO.getModuleCode())) {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(联系信息)");
            } else if ("empState".equals(companyAdditionalFieldsDO.getModuleCode())) {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(员工状态)");
            } else if ("onJob".equals(companyAdditionalFieldsDO.getModuleCode())) {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(在职信息)");
            } else if ("file".equals(companyAdditionalFieldsDO.getModuleCode())) {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(档案信息)");
            } else if ("retire".equals(companyAdditionalFieldsDO.getModuleCode())) {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(退休信息)");
            } else if ("annex".equals(companyAdditionalFieldsDO.getModuleCode())
                    || "archive".equals(companyAdditionalFieldsDO.getModuleCode())
                    || "leaveOffice".equals(companyAdditionalFieldsDO.getModuleCode())) {
                // 自定义字段中材料附件不能被选为合同附件变量
                continue;
//                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName() + "(材料附件)");
            } else {
                hrVariableListVO.setName(companyAdditionalFieldsDO.getFieldName());
            }
            // 自定义字段自己成一组
            hrVariableListVO.setType("additional");
            hrVariableListVO.setNeed(companyAdditionalFieldsDO.getIsNecessary());
            variableListVOList.add(hrVariableListVO);
        }

        // 原有所有信息
        if (!CollectionUtils.isEmpty(variableDOList)) {
            variableListVOList.addAll(variableDOList.parallelStream()
                    .map(variableDO -> variableDO.to(HrVariableListVO.class)).parallel()
                    .collect(Collectors.toList()));
        }
        if (StringUtils.isNotBlank(fieldName)) {
            variableListVOList = variableListVOList.parallelStream().filter(hrVariableListVO
                    -> hrVariableListVO.getName().contains(fieldName)).collect(Collectors.toList());
        }
        return variableListVOList;

    }

    @Override
    public int updateTemplate(TemplateUpdateDTO templateUpdateDTO, TokenUser tokenUser) {
        return templateService.update(templateUpdateDTO, tokenUser);
    }

    @Override
    public void updateTemplateVariable(TemplateVariableUpdateDTO templateVariableUpdateDTO, TokenUser tokenUser) {
        Long templateId = templateVariableUpdateDTO.getTemplateId();
        // 基础变量
        List<TemplateVariableDO> templateVariableDOList = templateVariableService
                .selectByTemplateId(templateId, tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(templateVariableDOList)) {
            templateVariableService.deleteByIds(tokenUser
                    , templateVariableDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        if (!CollectionUtils.isEmpty(templateVariableUpdateDTO.getVariableIds())) {
            List<TemplateVariableAddDTO> templateVariableAddDTOList = new ArrayList<>();
            for (Long variableId : templateVariableUpdateDTO.getVariableIds()) {
                TemplateVariableAddDTO templateVariableAddDTO = new TemplateVariableAddDTO();
                templateVariableAddDTO.setTemplateId(templateId);
                templateVariableAddDTO.setVariableId(variableId);
                templateVariableAddDTOList.add(templateVariableAddDTO);
            }
            templateVariableService.batchSave(templateVariableAddDTOList, tokenUser);
        }
        // 员工必填信息Ids
        List<NecessaryVariableDO> necessaryVariableDOList =
                necessaryVariableService.selectByTemplateId(templateId, tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(necessaryVariableDOList)) {
            necessaryVariableService.deleteByIds(tokenUser
                    , necessaryVariableDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        if (!CollectionUtils.isEmpty(templateVariableUpdateDTO.getNecessaryIds())) {
            List<NecessaryVariableAddDTO> necessaryVariableAddDTOList = new ArrayList<>();
            for (String necessaryId : templateVariableUpdateDTO.getNecessaryIds()) {
                NecessaryVariableAddDTO necessaryVariableAddDTO = new NecessaryVariableAddDTO();
                necessaryVariableAddDTO.setTemplateId(templateId);
                necessaryVariableAddDTO.setNecessaryId(necessaryId);
                necessaryVariableAddDTOList.add(necessaryVariableAddDTO);
            }
            necessaryVariableService.batchSave(necessaryVariableAddDTOList, tokenUser);
        }
        // 员工自定义信息Ids
        List<AdditionalVariableDO> additionalVariableDOList =
                additionalVariableService.selectByTemplateId(templateId, tokenUser.getSiteId());
        if (!CollectionUtils.isEmpty(additionalVariableDOList)) {
            additionalVariableService.deleteByIds(tokenUser
                    , additionalVariableDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
        }
        if (!CollectionUtils.isEmpty(templateVariableUpdateDTO.getAdditionalIds())) {
            List<AdditionalVariableAddDTO> additionalVariableAddDTOList = new ArrayList<>();
            for (Long additionalId : templateVariableUpdateDTO.getAdditionalIds()) {
                AdditionalVariableAddDTO additionalVariableAddDTO = new AdditionalVariableAddDTO();
                additionalVariableAddDTO.setTemplateId(templateId);
                additionalVariableAddDTO.setAdditionalId(additionalId);
                additionalVariableAddDTOList.add(additionalVariableAddDTO);
            }
            additionalVariableService.batchSave(additionalVariableAddDTOList, tokenUser);
        }
    }

    @Override
    public Integer setJpgToAppendix(Long contractId, TokenUser tokenUser) {
        // 根据合同id 下载合同的对应文件
        AppendixDO appendixDO = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        ParamException.notNull(appendixDO, "该合同没有文件，请重新发起合同");
        FileVersionDO fileVersionDO = siteFileService.download(Collections.singletonList(appendixDO.getPath()), null,
                tokenUser);
        ParamException.notNull(fileVersionDO, "文件不存在或已删除");
        File pdf = new File(fileVersionDO.getTempFilePath());
        // pdf转图片
        String fileName = System.currentTimeMillis() + ".jpg";
        String filePath = fileProperties.getTempPath() + File.separator + fileName;
        File file = new File(filePath);
        PdfUtil.pdf2multiImage(pdf.getAbsolutePath(), filePath, 80);
        MultipartFile[] multipartFiles = new MultipartFile[1];
        try {
            FileInputStream fileInputStreamTwo = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile("copy" + file.getName(), file.getName(),
                    ContentType.APPLICATION_OCTET_STREAM.toString(), fileInputStreamTwo);
            multipartFiles[0] = multipartFile;
        } catch (IOException e) {
            e.printStackTrace();
            throw new ParamException("文件转化失败!");
        }
        List<String> downloadReport = siteFileService.upload(multipartFiles, "attachment", tokenUser);
        HrException.isFalse(CollectionUtils.isEmpty(downloadReport), "上传文件出错了,请稍后重试或联系管理员");
        // 删除生成的图片
        file.delete();
        // 修改合同文件图片地址
        AppendixUpdateDTO appendixUpdateDTO = new AppendixUpdateDTO();
        appendixUpdateDTO.setId(appendixDO.getId());
        appendixUpdateDTO.setName(downloadReport.get(0));
        return appendixService.update(appendixUpdateDTO, tokenUser);
    }

    @Override
    public List<TemplateDO> selectByCompanyId(String companyId, String siteId) {
        List<TemplateDO> templateDOList = new ArrayList<>();
        CategoryQuery categoryQuery = new CategoryQuery();
        categoryQuery.setSubjectId(companyId);
        List<CategoryListVO> categoryListVOList = categoryService.select(categoryQuery, siteId);
        if (CollectionUtils.isEmpty(categoryListVOList)) {
            return templateDOList;
        }
        // 查询该分类下的所有合同模板
        for (CategoryListVO categoryListVO : categoryListVOList) {
            List<TemplateDO> templateDOS = templateService.selectByCategoryId(categoryListVO.getId(), siteId);
            if (CollectionUtils.isEmpty(templateDOS)) {
                continue;
            }
            templateDOList.addAll(templateDOS);
        }
        return templateDOList;
    }

    @Override
    public Integer updataTemplateDO(TemplateDO templateDO, TokenUser tokenUser) {
        return templateService.update(templateDO.to(TemplateUpdateDTO.class), tokenUser);
    }

    @Override
    public EmpInfoDO selectEmpInfoByContractId(Long contractId) {
        List<ContractDO> contractDOS = contractService.selectByIds(Collections.singletonList(contractId));
        if (CollectionUtils.isEmpty(contractDOS)) {
            return null;
        }
        ContractDO contractDO = contractDOS.get(0);
        if (contractDO == null) {
            return null;
        }
        String partyBAccountId = contractDO.getPartyBAccountId();
        AccountVO accountVO = accountService.selectById(partyBAccountId);
        if (accountVO == null) {
            return null;
        }
        // 用户id
        String userId = accountVO.getMemberId();
        return empInfoService.selectByUserIdAndCompanyIdNotValid(userId, contractDO.getSubjectId());
    }

    @Override
    public List<EmpInfoDO> selectEmpInfoByContractIds(List<Long> contractIds) {
        List<ContractDO> contractDOS = contractService.selectByIds(contractIds);
        if (CollectionUtils.isEmpty(contractDOS)) {
            return new ArrayList<>();
        }
        List<EmpInfoDO> empInfoDOList = new ArrayList<>();
        // 后期再优化for循环查询
        for (ContractDO contractDO : contractDOS) {
            String partyBAccountId = contractDO.getPartyBAccountId();
            AccountVO accountVO = accountService.selectById(partyBAccountId);
            if (accountVO == null) {
                continue;
            }
            // 用户id
            String userId = accountVO.getMemberId();
            EmpInfoDO empInfoDO = empInfoService.selectByUserId(userId, contractDO.getSubjectId(), contractDO.getSiteId());
            empInfoDOList.add(empInfoDO);
        }
        return empInfoDOList;
    }

    @Override
    public HeaderFieldsVO getSignedRecordHead(String companyId, TokenUser tokenUser) {
        List<HeaderFieldsListVO> canSee = new ArrayList<>();
        List<HeaderFieldsListVO> ignore = new ArrayList<>();
        List<HeaderFieldsDO> headerFieldsDOList = adminProviderService
                .selectHeaderFieldsByModuleCode(companyId, "signedRecord", tokenUser);
        if (!CollectionUtils.isEmpty(headerFieldsDOList)) {
            // 有修改记录
            for (HeaderFieldsDO headerFieldsDO : headerFieldsDOList) {
                HeaderFieldsListVO headerFieldsListVO = headerFieldsDO.to(HeaderFieldsListVO.class);
                if (headerFieldsDO.getIsOpen()) {
                    canSee.add(headerFieldsListVO);
                } else {
                    ignore.add(headerFieldsListVO);
                }
            }
        } else {
            // 默认表头
            Map<String, String> defaultHeadMap = new LinkedHashMap<>();
            defaultHeadMap.put("姓名", "partyBName");
            defaultHeadMap.put("身份证号", "idCard");
            defaultHeadMap.put("合同名称", "name");
            defaultHeadMap.put("合同开始时间", "publishDate");
            defaultHeadMap.put("合同结束时间", "endDate");
            defaultHeadMap.put("合同类型", "contractType");
            defaultHeadMap.put("签署状态", "status");
            defaultHeadMap.put("签署编号", "signNo");
            defaultHeadMap.put("合同备注", "contractNo");
            defaultHeadMap.put("手机号", "partyBPhone");
            defaultHeadMap.put("剩余有效时间", "remainingValidTime");
            defaultHeadMap.put("小程序是否展示", "appIsShow");
            defaultHeadMap.put("发起签署时间", "signDate");
            defaultHeadMap.put("员工签署时间", "empSignDate");
            defaultHeadMap.put("合同签署完成时间", "signFinishDate");
            int i = 0;
            for (String key : defaultHeadMap.keySet()) {
                HeaderFieldsListVO headerFieldsListVO = new HeaderFieldsListVO();
                headerFieldsListVO.setId(null);
                headerFieldsListVO.setSubjectType("companyId");
                headerFieldsListVO.setSourceId(null);
                headerFieldsListVO.setSubjectId(companyId);
                headerFieldsListVO.setModuleCode("signedRecord");
                headerFieldsListVO.setModuleType("签署记录");
                headerFieldsListVO.setIsOpen(i < 9);
                headerFieldsListVO.setIsEdit(i > 1);
                headerFieldsListVO.setFieldName(key);
                headerFieldsListVO.setFieldEnName(defaultHeadMap.get(key));
                headerFieldsListVO.setSort(i);
                if (i < 9) {
                    canSee.add(headerFieldsListVO);
                } else {
                    ignore.add(headerFieldsListVO);
                }
                i++;
            }
        }
        HeaderFieldsVO headerFieldsVO = new HeaderFieldsVO();
        headerFieldsVO.setCanSee(canSee);
        headerFieldsVO.setIgnore(ignore);
        return headerFieldsVO;
    }

    @Override
    public HeaderFieldsVO getAdminSignedRecordHead(String companyId, TokenUser tokenUser) {
        List<HeaderFieldsListVO> canSee = new ArrayList<>();
        List<HeaderFieldsListVO> ignore = new ArrayList<>();
        List<HeaderFieldsDO> headerFieldsDOList = adminProviderService
                .selectHeaderFieldsByModuleCode(companyId, "adminSignedRecord", tokenUser);
        if (!CollectionUtils.isEmpty(headerFieldsDOList)) {
            // 有修改记录
            for (HeaderFieldsDO headerFieldsDO : headerFieldsDOList) {
                HeaderFieldsListVO headerFieldsListVO = headerFieldsDO.to(HeaderFieldsListVO.class);
                if (headerFieldsDO.getIsOpen()) {
                    canSee.add(headerFieldsListVO);
                } else {
                    ignore.add(headerFieldsListVO);
                }
            }
        } else {
            // 默认表头
            Map<String, String> defaultHeadMap = new LinkedHashMap<>();
            defaultHeadMap.put("姓名", "partyBName");
            defaultHeadMap.put("身份证号", "idCard");
            defaultHeadMap.put("用工单位", "subjectName");
            defaultHeadMap.put("合同名称", "name");
            defaultHeadMap.put("合同开始时间", "publishDate");
            defaultHeadMap.put("合同结束时间", "endDate");
            defaultHeadMap.put("合同类型", "contractType");
            defaultHeadMap.put("签署状态", "status");
            defaultHeadMap.put("签署编号", "signNo");
            defaultHeadMap.put("合同备注", "contractNo");
            defaultHeadMap.put("手机号", "partyBPhone");
            defaultHeadMap.put("剩余有效时间", "remainingValidTime");
            defaultHeadMap.put("小程序是否展示", "appIsShow");
            defaultHeadMap.put("发起签署时间", "signDate");
            defaultHeadMap.put("员工签署时间", "empSignDate");
            defaultHeadMap.put("合同签署完成时间", "signFinishDate");
            int i = 0;
            for (String key : defaultHeadMap.keySet()) {
                HeaderFieldsListVO headerFieldsListVO = new HeaderFieldsListVO();
                headerFieldsListVO.setId(null);
                headerFieldsListVO.setSubjectType("companyId");
                headerFieldsListVO.setSourceId(null);
                headerFieldsListVO.setSubjectId(companyId);
                headerFieldsListVO.setModuleCode("signedRecord");
                headerFieldsListVO.setModuleType("签署记录");
                headerFieldsListVO.setIsOpen(i < 10);
                headerFieldsListVO.setIsEdit(i > 1);
                headerFieldsListVO.setFieldName(key);
                headerFieldsListVO.setFieldEnName(defaultHeadMap.get(key));
                headerFieldsListVO.setSort(i);
                if (i < 10) {
                    canSee.add(headerFieldsListVO);
                } else {
                    ignore.add(headerFieldsListVO);
                }
                i++;
            }
        }
        HeaderFieldsVO headerFieldsVO = new HeaderFieldsVO();
        headerFieldsVO.setCanSee(canSee);
        headerFieldsVO.setIgnore(ignore);
        return headerFieldsVO;
    }

    @Override
    public int updateContract(ContractUpdateDTO contractUpdateDTO, TokenUser tokenUser) {
        return contractService.update(contractUpdateDTO, tokenUser);
    }

    @Override
    public int selectCountByNameStart(String name, String partyBAccountId, Long notContractId) {
        return contractService.selectCountByNameStart(name, partyBAccountId, notContractId);
    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public List<ContractVO> publishTemplateSign(PublishSignDTO publishSignDTO, TokenUser tokenUser) {
        List<ContractVO> contractVOList = signService.publishTemplateSign(publishSignDTO, tokenUser);
        for (ContractVO contractVO : contractVOList) {
            // 没有乙方直接跳过
            if (contractVO.getPartyBPhone() == null) {
                continue;
            }
            // 同步花名册电子合同
            saveEmpContract(contractVO, tokenUser);
        }
        return contractVOList;
    }

    @Override
    public String publishTemplateSignAsync(PublishSignDTO publishSignDTO, TokenUser tokenUser) {
        String key = CacheKeyKit.getPublishTemplateSignKey(publishSignDTO.getSubjectId());
        RLock lock = redissonClient.getLock(key);
        if (lock.isLocked()) {
            throw new HrException("正在发起合同，请稍后操作");
        }

        long start = System.currentTimeMillis();
        log.info("主线程名称：{}, count:{}", Thread.currentThread().getName(), publishSignDTO.getMemberVariableList().size());
        if (StrKit.isEmpty(publishSignDTO.getMemberVariableList())) {
            throw new HrException("没有需要发起的合同，请重新选择人员发起合同！");
        }

        CompletableFuture.runAsync(() -> asyncCompanyFature.privatePublishTemplateSignAsync(publishSignDTO, tokenUser));

        long end = System.currentTimeMillis();
        log.info("主线程名称：{},异步发起合同结束, 耗时{} ", Thread.currentThread().getName(), (end - start) / 1000);
        return "success";
    }


    @Override
    public int deleteContractByIds(TokenUser tokenUser, Long... contractIds) {
        // 先删除合同表
        int delete = contractService.deleteByIds(tokenUser, contractIds);
        // 同步删除花名册电子合同
        List<EmpContractDO> empContractDOList = empContractService.selectByContractIds(Arrays.asList(contractIds));
        if (!CollectionUtils.isEmpty(empContractDOList)) {
            empContractService.deleteByIds(tokenUser, empContractDOList
                    .parallelStream().map(BaseDO::getId).toArray(String[]::new));
        }
        return delete;
    }

    /**
     * 同步花名册电子合同
     */
    @Override
    public void saveEmpContract(ContractVO contractVO, TokenUser tokenUser) {
        EmpInfoVO empInfoVO = empInfoService.selectByPhone(contractVO.getSubjectId(), contractVO.getPartyBPhone(),
                tokenUser.getSiteId());
        if (empInfoVO == null) {
            log.error("电子合同同步花名册失败,员工不存在,合同信息:{}", contractVO);
            return;
        }
        // 1、更新花名册员工合同状态
        EmpInfoDO empInfoDO = new EmpInfoDO();
        String empId = empInfoVO.getId();
        empInfoDO.setId(empId);
        empInfoDO.setContractsStatus(1);
        empInfoService.updateByIdSelective(empInfoDO, tokenUser.getSiteId(), tokenUser.getUserId());
        // 2、同步花名册合同信息
        Date publishDate = contractVO.getPublishDate();
        Date endDate = contractVO.getEndDate();
        int dateline = contractVO.getDateline() == null ? 0 : contractVO.getDateline();
        String fileType = contractVO.getFileType();
        // 获取当前开始结束日期的起始时间
        publishDate = DateKit8.getStartTime(publishDate);
        endDate = DateKit8.getStartTime(endDate);
        List<EmpContractDO> empContractDOList = empContractService.selectByDateAndType(publishDate, endDate, dateline
                , fileType, empId, tokenUser.getSiteId());
        // 过滤有合同Id的记录
        empContractDOList = empContractDOList.parallelStream().filter(empContractDO
                -> empContractDO.getContractId() == null).collect(Collectors.toList());
        // 查询同名合同 如果有 后面加序号
        String name = contractVO.getName();
        int countByNameStart = selectCountByNameStart(name, contractVO.getPartyBAccountId(), contractVO.getId());
        if (countByNameStart > 0) {
            name += "(" + countByNameStart + ")";
        }
        // 3、更新合同表合同名称
        ContractUpdateDTO contractUpdateDTO = new ContractUpdateDTO();
        contractUpdateDTO.setId(contractVO.getId());
        if (CollectionUtils.isEmpty(empContractDOList)) {
            // 没有4要素相同的员工合同 新建
            EmpContractAddDTO empContractAddDTO = new EmpContractAddDTO();
            empContractAddDTO.setContractId(contractVO.getId());
            // 获取合同员工id
            empContractAddDTO.setEmpId(empId);
            empContractAddDTO.setType(contractVO.getFileType());
            empContractAddDTO.setStartTime(publishDate);
            empContractAddDTO.setEndTime(endDate);
            empContractAddDTO.setTermYear(dateline / 12);
            empContractAddDTO.setName(name);
            int termMonth = dateline % 12;
            if (termMonth != 0) {
                empContractAddDTO.setTermMonth(dateline % 12);
            }
            empContractAddDTO.setCompanyId(contractVO.getSubjectId());
            empContractAddDTO.setRemark(contractVO.getContractNo());
            empContractService.save(empContractAddDTO, tokenUser);
            contractUpdateDTO.setName(name);
        } else {
            // 存在无对应签署合同 放入发起签署的合同Id
            EmpContractDO empContractDO = empContractDOList.get(0);
            if (StringUtils.isNotBlank(empContractDO.getName()) && !"未命名合同".equals(empContractDO.getName())) {
                // 花名册中合同名称不为空或默认值 同步修改合同表中合同名称
                contractUpdateDTO.setName(empContractDO.getName());
            } else {
                // 否则使用合同表中的合同名称 覆盖花名册中合同名称
                empContractDO.setName(name);
                contractUpdateDTO.setName(name);
            }
            contractUpdateDTO.setContractNo(empContractDO.getRemark());
            empContractDO.setContractId(contractVO.getId());
            empContractService.updateByIdSelective(empContractDO, tokenUser.getSiteId(), tokenUser.getUserId());
        }
        updateContract(contractUpdateDTO, tokenUser);
        // 4、修改上传文件的名称
        updateFileName(contractVO.getId(), name, tokenUser);
    }

    @Override
    public void updateFileName(Long contractId, String contractName, TokenUser tokenUser) {
        // 修改上传文件的名称
        AppendixDO appendixDO = appendixService.selectByContractId(contractId, tokenUser.getSiteId());
        if (appendixDO != null) {
            String path = appendixDO.getPath();
            log.info("path:" + path);
            if (!path.contains("/")) {
                throw new ParamException("下载地址有误");
            }
            //https://abc.com/file/api/v1/site/view/inline/40659446784858880/40659446784858624/origin(15).pdf
            path = path.substring(0, path.lastIndexOf("/"));
            log.info("path:" + path);
            // 40659446784858624
            String fileId = path.substring(path.lastIndexOf("/") + 1);
            path = path.substring(0, path.lastIndexOf("/"));
            log.info("fileId:" + fileId);
            // 40659446784858624
            String fileUniId = path.substring(path.lastIndexOf("/") + 1);
            log.info("fileUniId:" + fileUniId);
            ParamException.hasText(fileUniId, "下载地址有误，没有获取到fileUniId");

            // 下载文件名 合同名(身份证号).pdf
            String fileName = contractName;
            // 合同名称
            EmpInfoDO empInfoDO = selectEmpInfoByContractId(contractId);
            if (empInfoDO == null) {
                log.error("员工被物理删除，合同Id：{}", contractId);
            } else {
                fileName += "(" + empInfoDO.getIdCard() + ")";
            }

            // 修改文件版本的名称
            FileVersionDO fileVersionDO = fileVersionService.findByFileUniId(fileUniId, tokenUser.getSiteId());
            if (fileVersionDO != null) {
                // 上传文件的文件名
                String fileVersionName = fileVersionDO.getName();
                // 放入后缀名
                fileVersionDO.setName(fileName + "." + fileVersionDO.getExt());
                fileVersionService.updateById(fileVersionDO, fileVersionDO.getSiteId(), tokenUser.getSiteId());
            }
            // 修改文件的名称
            FileDO fileDO = fileService.selectDOById(fileId, tokenUser.getSiteId());
            if (fileDO != null) {
                // 放入后缀名
                fileDO.setName(fileName + "." + fileDO.getExt());
                fileService.updateById(fileDO, fileDO.getSiteId(), tokenUser.getSiteId());
            }
        }

    }

    @Override
    @Transactional(value = ContractConstants.TX, rollbackFor = Exception.class)
    public List<ContractGroupListVO> selectAndSynchronizeContractGroup(ContractGroupQuery contractGroupQuery, TokenUser tokenUser) {
        // 非租户级跳过
        if (contractGroupQuery.getSubjectId() == null || !"0".equals(contractGroupQuery.getSubjectId())) {
            return contractGroupService.select(contractGroupQuery, tokenUser).parallelStream()
                    .sorted(Comparator.comparing(ContractGroupListVO::getIsSystem)
                            .thenComparing(ContractGroupListVO::getGmtCreate).reversed())
                    .collect(Collectors.toList());
        }
        // 仅在查询租户级合同分组时触发同步
        List<ContractGroupDO> contractGroupDOList = contractGroupService.selectIsSystem(tokenUser.getSiteId());
        // 有租户下系统默认分组 跳过
        if (!CollectionUtils.isEmpty(contractGroupDOList)) {
            return contractGroupService.select(contractGroupQuery, tokenUser).parallelStream()
                    .sorted(Comparator.comparing(ContractGroupListVO::getIsSystem)
                            .thenComparing(ContractGroupListVO::getGmtCreate).reversed())
                    .collect(Collectors.toList());
        }
        // 若租户下没有系统默认分组 创建并同步创建系统合同类型及模板
        ContractGroupAddDTO contractGroupAddDTO = new ContractGroupAddDTO();
        contractGroupAddDTO.setIsSystem(Boolean.TRUE);
        contractGroupAddDTO.setName("系统提供合同模板");
        contractGroupAddDTO.setSubjectId("0");
        contractGroupAddDTO.setSort(-1);
        ContractGroupVO contractGroupVO = contractGroupService.save(contractGroupAddDTO, tokenUser);
        // 系统级合同类型 只查开启的
        Example example = new Example(CategoryDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", ADMIN)
                .andEqualTo("isOpen", Boolean.TRUE);
        List<CategoryDO> adminCategoryDOList = categoryDAO.selectByExample(example);
        // 没有系统默认模板 跳过
        if (CollectionUtils.isEmpty(adminCategoryDOList)) {
            return contractGroupService.select(contractGroupQuery, tokenUser).parallelStream()
                    .sorted(Comparator.comparing(ContractGroupListVO::getIsSystem)
                            .thenComparing(ContractGroupListVO::getGmtCreate).reversed())
                    .collect(Collectors.toList());
        }
        for (CategoryDO categoryDO : adminCategoryDOList) {
            CategoryAddDTO categoryAddDTO = categoryDO.to(CategoryAddDTO.class);
            categoryAddDTO.setGroupId(contractGroupVO.getId());
            categoryAddDTO.setSourceId(categoryDO.getId() + "");
            CategoryVO siteCategory = categoryService.save(categoryAddDTO, tokenUser);
            // 更新模板信息 categoryService.save 方法中创建了模板
            Example templateExample = new Example(TemplateDO.class);
            templateExample.and().andEqualTo("categoryId", siteCategory.getId());
            templateExample.orderBy("gmtCreate").desc();
            // 租户模板
            List<TemplateDO> siteTemplateList = templateDAO.selectByExample(templateExample);
            // 系统模板
            Example adminTemplateExample = new Example(TemplateDO.class);
            adminTemplateExample.and().andEqualTo("categoryId", categoryDO.getId());
            adminTemplateExample.orderBy("gmtCreate").desc();
            // 租户模板
            List<TemplateDO> adminTemplateDOList = templateDAO.selectByExample(adminTemplateExample);
            // 只更新第一个模板
            if (CollectionUtils.isEmpty(siteTemplateList) || CollectionUtils.isEmpty(adminTemplateDOList)) {
                continue;
            }
            TemplateDO siteTemplateDO = siteTemplateList.get(0);
            Long siteTemplateId = siteTemplateDO.getId();
            TemplateDO adminTemplateDO = adminTemplateDOList.get(0);
            TemplateUpdateDTO updateDTO = adminTemplateDO.to(TemplateUpdateDTO.class);
            // 保留原有合同分类Id
            updateDTO.setId(siteTemplateId);
            updateDTO.setCategoryId(siteTemplateDO.getCategoryId());
            templateService.update(updateDTO, tokenUser);
            // 删除租户级基础变量
            Example templateVariableExample = new Example(TemplateVariableDO.class);
            templateVariableExample.and().andEqualTo("templateId", siteTemplateId);
            templateVariableDAO.deleteByExample(templateVariableExample);
            // 系统级基础变量
            Example adminExample = new Example(TemplateVariableDO.class);
            adminExample.and().andEqualTo("templateId", adminTemplateDO.getId());
            List<TemplateVariableDO> templateVariableDOList = templateVariableDAO.selectByExample(adminExample);
            if (!CollectionUtils.isEmpty(templateVariableDOList)) {
                List<TemplateVariableAddDTO> templateVariableAddDTOList = new ArrayList<>();
                List<Long> adminVariableIds = templateVariableDOList.parallelStream()
                        .map(TemplateVariableDO::getVariableId).distinct().collect(Collectors.toList());
                for (Long variableId : adminVariableIds) {
                    TemplateVariableAddDTO templateVariableAddDTO = new TemplateVariableAddDTO();
                    templateVariableAddDTO.setTemplateId(siteTemplateId);
                    templateVariableAddDTO.setVariableId(variableId);
                    templateVariableAddDTOList.add(templateVariableAddDTO);
                }
                templateVariableService.batchSave(templateVariableAddDTOList, tokenUser);
            }
            // 系统模板变量
            List<DefaultVariableDO> defaultVariableDOList = defaultVariableService.selectByTemplateId(adminTemplateDO.getId());
            // 删除用工单位原有合同类型下默认信息变量
            List<NecessaryVariableDO> necessaryVariableDOList = necessaryVariableService.selectByTemplateIds(
                    Collections.singletonList(siteTemplateId), tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(necessaryVariableDOList)) {
                necessaryVariableService.deleteByIds(tokenUser
                        , necessaryVariableDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
            }
            // 合同模板关联默认字段转化为必填字段
            savesiteNecessaryVariable(Collections.singletonList(siteTemplateId), defaultVariableDOList, tokenUser);
            // 系统印章位置
            List<SealPositionDO> adminSealPositionList = sealPositionService
                    .selectByTemplateId(adminTemplateDO.getId(), ADMIN);
            // 原有印章位置删除
            List<SealPositionDO> siteSealPositionDOList = sealPositionService
                    .selectByTemplateId(adminTemplateDO.getId(), tokenUser.getSiteId());
            if (!CollectionUtils.isEmpty(siteSealPositionDOList)) {
                sealPositionService.deleteByIds(tokenUser, siteSealPositionDOList.parallelStream().map(BaseDO::getId).toArray(Long[]::new));
            }
            List<SealPositionAddDTO> sealPositionAddDTOList = new ArrayList<>();
            for (SealPositionDO sealPositionDO : adminSealPositionList) {
                SealPositionAddDTO sealPositionAddDTO = sealPositionDO.to(SealPositionAddDTO.class);
                sealPositionAddDTO.setTemplateId(siteTemplateId);
                sealPositionAddDTOList.add(sealPositionAddDTO);
            }
            sealPositionService.batchSave(sealPositionAddDTOList, tokenUser);
        }
        return contractGroupService.select(contractGroupQuery, tokenUser).parallelStream()
                .sorted(Comparator.comparing(ContractGroupListVO::getIsSystem)
                        .thenComparing(ContractGroupListVO::getGmtCreate).reversed())
                .collect(Collectors.toList());
    }

    @Override
    public int updateCategory(CategoryUpdateDTO categoryUpdateDTO, TokenUser tokenUser) {
        return categoryService.update(categoryUpdateDTO, tokenUser);
    }

    @Override
    public String demoAsync(PublishSignDTO publishSignDTO, TokenUser tokenUser) {
        log.info("主线程：{}，开始时间", Thread.currentThread().getName(), System.currentTimeMillis());
        List<MemberVariableDTO> list = publishSignDTO.getMemberVariableList();
        if (!CollectionUtils.isEmpty(list)) {
            //计数器
            CountDownLatch countDownLatch = new CountDownLatch(list.size());
            for (MemberVariableDTO memberVariableDTO : list) {
                //多线程执行
                ContractVO contractVO = asyncContractProvider.demoAsync(memberVariableDTO, countDownLatch);
            }
            //主线程阻塞一下
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        log.info("主线程：{}，结束时间", Thread.currentThread().getName(), System.currentTimeMillis());
        return null;
    }

    @Override
    public void enterpriseSignAgain(Long contractId, String companyId, TokenUser tokenUser) {
        signService.contractSignAgain(contractId, companyId, tokenUser);
    }

    @Override
    public List<PartySignAccountVO> findPartySignAccount(Long contractId, String siteId) {
        return partySignService.selectAccountByContractId(contractId, siteId);
    }

    @Override
    public List<AppendixDO> appendixListByContractIds(List<Long> contractIdList, String siteId) {
        return appendixService.listByContractIds(contractIdList, siteId);
    }

    @Override
    public AccountDO findAccount(AccountType type, String memberId) {
        return accountService.findAccount(type, memberId);
    }

    @Override
    public List<SealPositionDO> selectByContractId(Long contractId, TokenUser tokenUser) {
        ContractVO contractVO = contractService.selectById(contractId, tokenUser.getSiteId());
        SealPositionQuery query = new SealPositionQuery();
        query.setTemplateId(contractVO.getTemplateId());
        return sealPositionService.selectDO(query, tokenUser.getSiteId());
    }

    @Override
    public String getUrl(Long contractId, Integer type, TokenUser tokenUser) {
        ContractVO contractVO = contractService.selectById(contractId, tokenUser.getSiteId());
        String companyId = contractVO.getSubjectId();
        List<PartySignDO> partySignList = partySignService.selectByContractId(contractId, tokenUser.getSiteId());
        List<String> accountIds = partySignList.stream().map(PartySignDO::getAccountId).collect(Collectors.toList());
        List<AccountVO> accounts = accountService.selectByIds(accountIds);
        // 获取合同签署双方
        if (Objects.equals(type, 1)) {
            String object = cache.get(KEY_PERSON + contractId);
            if (object != null) {
                return object;
            }
            for (AccountVO account : accounts) {
                if (account.getType() != 1) {
                    continue;
                }
                EmpInfoDO empInfoDO = empInfoService.selectByUserId(account.getMemberId(), companyId, tokenUser.getSiteId());
                String url = junZiQianProperties.getServicesUrl() + JunZiQianConstants.URL_CONTRACT_GET_URL;
                UrlVO urlVO = new UrlVO();
                urlVO.setApplyNo(contractVO.getContractNo());
                urlVO.setFullName(empInfoDO.getName());
                urlVO.setIdentityCard(empInfoDO.getIdCard());
                urlVO.setIdentityType(1);
                urlVO.setUseCustomDomain(1);
                JunZiQianResult result = JunZiQianUtil.send(urlVO, junZiQianProperties, url);
                String contractUrl = String.valueOf(result.getData());
                cache.set(KEY_PERSON + contractId, contractUrl, 7 * 24 * 60 * 60 * 1000);
                return contractUrl;
            }
        } else {
            String object = cache.get(KEY_COMPANY + contractId);
            if (object != null) {
                return object;
            }
            for (AccountVO account : accounts) {
                if (account.getType() != 2) {
                    continue;
                }
                CompanyContractQuotaDO quotaDO = companyContractQuotaService.selectByEmail(account.getAccountId());
                String url = junZiQianProperties.getServicesUrl() + JunZiQianConstants.URL_CONTRACT_GET_URL;
                UrlVO urlVO = new UrlVO();
                urlVO.setApplyNo(contractVO.getContractNo());
                urlVO.setFullName(quotaDO.getCompanyName());
                urlVO.setIdentityCard(quotaDO.getOrganizationRegNo());
                urlVO.setIdentityType(12);
                JunZiQianResult result = JunZiQianUtil.send(urlVO, junZiQianProperties, url);
                String contractUrl = String.valueOf(result.getData());
                cache.set(KEY_COMPANY + contractId, contractUrl, 7 * 24 * 60 * 60 * 1000);
                return contractUrl;
            }
        }
        return "";
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public void personSign(Long contractId, byte[] stream, Boolean showDiffSite, TokenUser tokenUser) {
        signService.personSign2(contractId, stream, showDiffSite, tokenUser);
    }

    @Override
    public void companySign(Long contractId, String companyId, byte[] bytes, boolean showDiffSite, TokenUser tokenUser) {
        signService.contractSignAgain2(contractId, companyId, bytes, showDiffSite, tokenUser);
    }

    @Override
    public ContractDO selectByContractNo(String contractNo) {
        return contractService.selectByContractNo(contractNo);
    }

    @Override
    public List<SealListVO> selectSeal(SealQuery sealQuery, String siteId) {
        List<SealListVO> vos = sealService.select(sealQuery, siteId);
        if (!CollectionUtils.isEmpty(vos)) {
            Object[] array = vos.stream().map(SealListVO::getOrganizationId).filter(Objects::nonNull).toArray(Object[]::new);
            if (array.length > 0) {
                List<CompanyContractQuotaDO> dos = companyContractQuotaService.listByIds(siteId, array);
                Map<Long, String> map = dos.stream().collect(Collectors.toMap(BaseDO::getId, CompanyContractQuotaDO::getCompanyName));
                for (SealListVO vo : vos) {
                    vo.setOrganizationName(map.get(vo.getOrganizationId()));
                }
            }

        }
        return vos;
    }

    //@Override
    //@Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    //public Integer cancel(Long contractId, TokenUser tokenUser) {
    //    // 查询合同id
    //    ContractVO contractVO = contractService.selectById(contractId, tokenUser.getSiteId());
    //    String url = junZiQianProperties.getServicesUrl() + JunZiQianConstants.URL_CONTRACT_CANCEL;
    //    Map<String, String> params = new HashMap<>();
    //    params.put("applyNo", contractVO.getContractNo());
    //    JunZiQianUtil.send(params, junZiQianProperties, url);
    //    ContractDO contractDO = new ContractDO();
    //    contractDO.setId(contractId);
    //    contractDO.setStatus(ContractsStatus.CANCEL.getValue());
    //    // 删除
    //    return contractService.updateByIdSelective(contractDO);
    //}

    /**
     * 将查询到的字段转换为excel列表展示的字段
     *
     * @param contractExportVO 转换前的VO
     * @return contractExportVO  转换后的VO
     */
    private ContractExportVO setFieldValue(ContractExportVO contractExportVO) {
        // 小程序是否展示
        Boolean appIsShow = contractExportVO.getAppIsShow();
        // 合同类型
        String fileType = contractExportVO.getFileType();
        // 签署状态  1待员工签署，2待公司签署，3签署完成，-1签署失败
        Integer status = contractExportVO.getStatus();

        if (appIsShow) {
            contractExportVO.setAppIsShowString("是");
        } else {
            contractExportVO.setAppIsShowString("否");
        }
        FileType fileType1 = FileType.find(fileType);
        if (fileType1 != null) {
            contractExportVO.setFileTypeString(fileType1.getDesc());
        }

        if (1 == status) {
            contractExportVO.setStatusString("待员工签署");
        } else if (2 == status) {
            contractExportVO.setStatusString("待公司签署");
        } else if (3 == status) {
            contractExportVO.setStatusString("签署完成");
        } else if (-1 == status) {
            contractExportVO.setStatusString("合同过期未签署");
        }

        if (null != contractExportVO.getPublishDate()) {
            contractExportVO.setPublishDateString(DateKit8.format2Date(contractExportVO.getPublishDate()));
        }
        if (null != contractExportVO.getEndDate()) {
            contractExportVO.setEndDateString(DateKit8.format2Date(contractExportVO.getEndDate()));
        }

        return contractExportVO;
    }


}
